diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,20 @@
 # h-raylib changelog
 
+## Changelog guidelines (apply to `5.5.0.0` onward)
+
+Changes ported from the upstream raylib code are not mentioned unless they are breaking changes. Changes and bug fixes in the h-raylib API are mentioned, breaking and non-breaking. Internal changes that do not change the API or affect functionality are not mentioned (e.g. performance improvements).
+
+### Versioning scheme
+
+h-raylib's version numbers do not follow the usual format. The first two numbers in the version track the underlying C raylib version. For example, `5.1.x.x` versions use raylib 5.1 under the hood. The third number represents breaking changes (renamed/deleted functions or modules). The last number represents non-breaking changes (new functions or modules, bug fixes, etc). The safest version bound format to use is `h-raylib >=x.y.z.w && <x.y.(z+1)` (instead of the usual `^>=` bound).
+
+## Version 5.5.1.0
+_11 October 2024_
+
+- **BREAKING CHANGE**: `set*Callback` functions are no longer managed and do not return `IO C'*Callback` values
+- **BREAKING CHANGE**: Removed `loadImageSvg` (upstream change in raylib)
+- \[[#58](https://github.com/Anut-py/h-raylib/issues/58)\] Added support for `setTraceLogCallback`
+
 ## Version 5.5.0.0
 _12 July 2024_
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -12,27 +12,27 @@
 
 [DOCUMENTATION.md](https://github.com/Anut-py/h-raylib/blob/master/DOCUMENTATION.md) has information that is specific to h-raylib.
 
-# h-raylib roadmap
+## h-raylib roadmap
 
 This is a list of features that may be added to this project. Contributors are welcome to help implement these.
 
-## Pending
+### Pending
 
 Items which have not yet been worked on. Feel free to work on one of these.
 
 - Bind `rgestures`
 
-## In progress
+### In progress
 
 - Add web build support \[[#4](https://github.com/Anut-py/h-raylib/issues/4)\]
 
-## Implemented
+### Implemented
 
 Items which have been completed but not published to hackage.
 
 (none)
 
-## Published
+### Published
 
 Items which have been published to hackage.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # h-raylib: Haskell bindings for Raylib
 
-[![cabal-build](https://github.com/Anut-py/h-raylib/actions/workflows/cabal-build.yml/badge.svg)](https://github.com/Anut-py/h-raylib/actions/workflows/cabal-build.yml) [![nix-build](https://github.com/Anut-py/h-raylib/actions/workflows/nix-build.yml/badge.svg)](https://github.com/Anut-py/h-raylib/actions/workflows/nix-build.yml) ![Hackage Version](https://img.shields.io/hackage/v/h-raylib)
+[![cabal-build](https://github.com/Anut-py/h-raylib/actions/workflows/cabal-build.yml/badge.svg)](https://github.com/Anut-py/h-raylib/actions/workflows/cabal-build.yml) [![nix-build](https://github.com/Anut-py/h-raylib/actions/workflows/nix-build.yml/badge.svg)](https://github.com/Anut-py/h-raylib/actions/workflows/nix-build.yml) [![Hackage Version](https://img.shields.io/hackage/v/h-raylib)](https://hackage.haskell.org/package/h-raylib)
 
 
 This library includes Haskell bindings to the [Raylib](https://www.raylib.com/) library.
@@ -109,7 +109,7 @@
 - When I try to compile an h-raylib program I get the error `Missing (or bad) C libraries: gcc_eh`
   - See [#36](https://github.com/Anut-py/h-raylib/issues/36)
 
-If you find a bug, please [create an issue on GitHub](https://github.com/Anut-py/h-raylib/issues).
+If you find a bug, please [create an issue on GitHub](https://github.com/Anut-py/h-raylib/issues). There are probably some bindings that are incomplete or do not work properly, so if something seems wrong then it is most likely a bug.
 
 If you have a question about the library that is not related to a bug, ask it on [GitHub discussions](https://github.com/Anut-py/h-raylib/discussions) or in the [Haskell GameDev Discord server](https://discord.gg/aKHNgxc59t).
 
diff --git a/default.nix b/default.nix
--- a/default.nix
+++ b/default.nix
@@ -3,7 +3,7 @@
 }:
 mkDerivation {
   pname = "h-raylib";
-  version = "5.5.0.0";
+  version = "5.5.1.0";
   src = ./.;
   isLibrary = true;
   isExecutable = buildExamples;
diff --git a/examples/basic-callbacks/src/Main.hs b/examples/basic-callbacks/src/Main.hs
--- a/examples/basic-callbacks/src/Main.hs
+++ b/examples/basic-callbacks/src/Main.hs
@@ -2,9 +2,9 @@
 module Main where
 
 import Paths_h_raylib (getDataFileName)
-import Raylib.Core (clearBackground, initWindow, setTargetFPS, windowShouldClose, closeWindow, setLoadFileTextCallback, loadFileText)
+import Raylib.Core (clearBackground, initWindow, setTargetFPS, windowShouldClose, closeWindow, setLoadFileTextCallback, setTraceLogCallback, loadFileText)
 import Raylib.Core.Text (drawText)
-import Raylib.Util (drawing, raylibApplication, WindowResources, managed)
+import Raylib.Util (drawing, raylibApplication, WindowResources)
 import Raylib.Util.Colors (black, rayWhite)
 
 filePath :: String
@@ -14,9 +14,10 @@
 
 startup :: IO AppState
 startup = do
+  setTraceLogCallback (\logLevel text -> putStrLn (show logLevel ++ ": " ++ text))
   window <- initWindow 600 450 "raylib [core] example - basic callbacks"
   setTargetFPS 60
-  _ <- managed window $ setLoadFileTextCallback (\s -> putStrLn ("opening file: " ++ s) >> readFile s)
+  setLoadFileTextCallback (\s -> putStrLn ("opening file: " ++ s) >> readFile s)
   text <- loadFileText =<< getDataFileName filePath
   return (text, window)
 
diff --git a/flake.nix b/flake.nix
--- a/flake.nix
+++ b/flake.nix
@@ -9,10 +9,10 @@
       forAllSystems' = nixpkgs.lib.genAttrs;
       forAllSystems = forAllSystems' supportedSystems;
 
-      raylibRev = "8d5374a443509036063a350d1649408e009425e7";
-      raylibHash = "sha256-kYuqEUkyw8dngy5yBneNn+Br0xQr6NagP/s2CPM+q44=";
-      rayguiRev = "3fb9d77a67243497e10ae0448374a52a2a65e26f";
-      rayguiHash = "sha256-aWfXimbGU7RYqqOd44GmH3jPN8gcN4D/ygB71jIEr2c=";
+      raylibRev = "f5328a9bb63a0e0eca7dead15cfa01a3ec1417c2";
+      raylibHash = "sha256-gAv1jfMdbKWnlIqqBddVn+WE0BOIARMmagpK61bxVnU=";
+      rayguiRev = "1e03efca48c50c5ea4b4a053d5bf04bad58d3e43";
+      rayguiHash = "sha256-PzQZxCz63EPd7sVFBYY0T1s9jA5kOAxF9K4ojRoIMz4=";
 
       pkgsForSystem =
         system:
diff --git a/h-raylib.cabal b/h-raylib.cabal
--- a/h-raylib.cabal
+++ b/h-raylib.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               h-raylib
-version:            5.5.0.0
+version:            5.5.1.0
 synopsis:           Raylib bindings for Haskell
 category:           graphics
 description:
@@ -17,6 +17,10 @@
   DOCUMENTATION.md
   README.md
 
+-- Requires >= 9.6.5 on Mac because this bug fix is required: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12079
+-- Works with >= 9.2.8 on Windows
+tested-with: GHC >= 9.6.5
+
 extra-source-files:
   default.nix
   flake.nix
@@ -108,15 +112,14 @@
       , base
       , h-raylib
 
-    other-modules: Paths_h_raylib
-    autogen-modules: Paths_h_raylib
-
+    other-modules:    Paths_h_raylib
+    autogen-modules:  Paths_h_raylib
     other-extensions:
       BangPatterns
       TemplateHaskell
-      
-    ghc-options: -Wall
 
+    ghc-options:      -Wall
+
     if flag(platform-web)
       ghc-options:
         -no-hs-main -optl-mexec-model=reactor
@@ -257,9 +260,9 @@
     ScopedTypeVariables
     TemplateHaskell
     TemplateHaskellQuotes
-  
-  ghc-options: -Wall
 
+  ghc-options:      -Wall
+
   if (flag(platform-windows) || (flag(detect-platform) && os(windows)))
     if flag(mingw-cross)
       extra-libraries:
@@ -310,8 +313,8 @@
 
   else
 
-  -- Unsupported OS, do nothing. If you can get it working on an
-  -- OS that isn't listed here, please add it here.
+  --   Unsupported OS, do nothing. If you can get it working on an
+  --   OS that isn't listed here, please add it here.
   if flag(platform-nixos)
     extra-libraries: raylib
 
@@ -322,7 +325,9 @@
     c-sources:   lib/web.c
 
   else
-    cc-options: -DPLATFORM_DESKTOP_GLFW -Wno-int-to-void-pointer-cast
+    cc-options:
+      -DPLATFORM_DESKTOP_GLFW -Wno-int-to-void-pointer-cast
+
     c-sources:
       lib/rgui_bindings.c
       lib/rl_bindings.c
diff --git a/lib/rl_bindings.c b/lib/rl_bindings.c
--- a/lib/rl_bindings.c
+++ b/lib/rl_bindings.c
@@ -3,6 +3,7 @@
  */
 
 #include "rl_bindings.h"
+#include <stdio.h>
 
 RLBIND void SetWindowIcon_(Image *a)
 {
@@ -642,13 +643,6 @@
     return ptr;
 }
 
-RLBIND Image *LoadImageSvg_(char *a, int b, int c)
-{
-    Image *ptr = (Image *)malloc(sizeof(Image));
-    *ptr = LoadImageSvg(a, b, c);
-    return ptr;
-}
-
 RLBIND Image *LoadImageAnim_(char *a, int *b)
 {
     Image *ptr = (Image *)malloc(sizeof(Image));
@@ -1144,6 +1138,13 @@
     return ptr;
 }
 
+RLBIND Color *ColorLerp_(Color *a, Color *b, float c)
+{
+    Color *ptr = (Color *)malloc(sizeof(Color));
+    *ptr = ColorLerp(*a, *b, c);
+    return ptr;
+}
+
 RLBIND Color *GetColor_(unsigned int a)
 {
     Color *ptr = (Color *)malloc(sizeof(Color));
@@ -1443,6 +1444,16 @@
     DrawModelWiresEx(*a, *b, *c, d, *e, *f);
 }
 
+RLBIND void DrawModelPoints_(Model *a, Vector3 *b, float c, Color *d)
+{
+    DrawModelPoints(*a, *b, c, *d);
+}
+
+RLBIND void DrawModelPointsEx_(Model *a, Vector3 *b, Vector3 *c, float d, Vector3 *e, Color *f)
+{
+    DrawModelPointsEx(*a, *b, *c, d, *e, *f);
+}
+
 RLBIND void DrawBoundingBox_(BoundingBox *a, Color *b)
 {
     DrawBoundingBox(*a, *b);
@@ -1614,6 +1625,11 @@
     return IsModelAnimationValid(*a, *b);
 }
 
+RLBIND void UpdateModelAnimationBoneMatrices_(Model *a, ModelAnimation *b, int c)
+{
+    UpdateModelAnimationBoneMatrices(*a, *b, c);
+}
+
 RLBIND bool CheckCollisionSpheres_(Vector3 *a, float b, Vector3 *c, float d)
 {
     return CheckCollisionSpheres(*a, b, *c, d);
@@ -2362,6 +2378,22 @@
     OpenURL(a);
 }
 
+TraceLogCallback_ customCallback;
+
+void CustomCallback(int logLevel, const char *text, va_list args)
+{
+    size_t n = vsnprintf(NULL, 0, text, args) + 1;
+    char *buffer = malloc(n);
+    vsnprintf(buffer, n, text, args);
+    customCallback(logLevel, buffer);
+}
+
+RLBIND void SetTraceLogCallback_(TraceLogCallback_ a)
+{
+    customCallback = a;
+    SetTraceLogCallback(&CustomCallback);
+}
+
 RLBIND void SetLoadFileDataCallback_(LoadFileDataCallback a)
 {
     SetLoadFileDataCallback(a);
@@ -2470,6 +2502,11 @@
 RLBIND const char *GetApplicationDirectory_()
 {
     return GetApplicationDirectory();
+}
+
+RLBIND int MakeDirectory_(const char *a)
+{
+    return MakeDirectory(a);
 }
 
 RLBIND bool ChangeDirectory_(const char *a)
diff --git a/lib/rl_bindings.h b/lib/rl_bindings.h
--- a/lib/rl_bindings.h
+++ b/lib/rl_bindings.h
@@ -10,6 +10,8 @@
 
 #include "rl_common.h"
 
+typedef void (*TraceLogCallback_)(int logLevel, char *text);
+
 void SetWindowIcon_(Image *a);
 
 Vector2 *GetMonitorPosition_(int a);
@@ -238,8 +240,6 @@
 
 Image *LoadImageRaw_(char *a, int b, int c, int d, int e);
 
-Image *LoadImageSvg_(char *a, int b, int c);
-
 Image *LoadImageAnim_(char *a, int *b);
 
 Image *LoadImageAnimFromMemory_(char *a, unsigned char *b, int c, int *d);
@@ -404,6 +404,8 @@
 
 Color *ColorAlphaBlend_(Color *a, Color *b, Color *c);
 
+Color *ColorLerp_(Color *a, Color *b, float c);
+
 Color *GetColor_(unsigned int a);
 
 Color *GetPixelColor_(void *a, int b);
@@ -510,6 +512,10 @@
 
 void DrawModelWiresEx_(Model *a, Vector3 *b, Vector3 *c, float d, Vector3 *e, Color *f);
 
+void DrawModelPoints_(Model *a, Vector3 *b, float c, Color *d);
+
+void DrawModelPointsEx_(Model *a, Vector3 *b, Vector3 *c, float d, Vector3 *e, Color *f);
+
 void DrawBoundingBox_(BoundingBox *a, Color *b);
 
 void DrawBillboard_(Camera3D *a, Texture *b, Vector3 *c, float d, Color *e);
@@ -568,6 +574,8 @@
 
 bool IsModelAnimationValid_(Model *a, ModelAnimation *b);
 
+void UpdateModelAnimationBoneMatrices_(Model *a, ModelAnimation *b, int c);
+
 bool CheckCollisionSpheres_(Vector3 *a, float b, Vector3 *c, float d);
 
 bool CheckCollisionBoxes_(BoundingBox *a, BoundingBox *b);
@@ -856,6 +864,8 @@
 
 void OpenURL_(const char *a);
 
+void SetTraceLogCallback_(TraceLogCallback_ a);
+
 void SetLoadFileDataCallback_(LoadFileDataCallback a);
 
 void SetSaveFileDataCallback_(SaveFileDataCallback a);
@@ -899,6 +909,8 @@
 const char *GetWorkingDirectory_();
 
 const char *GetApplicationDirectory_();
+
+int MakeDirectory_(const char *a);
 
 bool ChangeDirectory_(const char *a);
 
diff --git a/lib/rlgl_bindings.c b/lib/rlgl_bindings.c
--- a/lib/rlgl_bindings.c
+++ b/lib/rlgl_bindings.c
@@ -21,6 +21,11 @@
   rlSetUniformMatrix(locIndex, *mat);
 }
 
+RLBIND void rlSetUniformMatrices_(int locIndex, const Matrix *mat, int count)
+{
+  rlSetUniformMatrices(locIndex, mat, count);
+}
+
 RLBIND Matrix *rlGetMatrixProjectionStereo_(int eye)
 {
   Matrix *ptr = (Matrix *)malloc(sizeof(Matrix));
diff --git a/lib/rlgl_bindings.h b/lib/rlgl_bindings.h
--- a/lib/rlgl_bindings.h
+++ b/lib/rlgl_bindings.h
@@ -12,6 +12,8 @@
 
 void rlSetUniformMatrix_(int locIndex, Matrix *mat);
 
+void rlSetUniformMatrices_(int locIndex, const Matrix *mat, int count);
+
 Matrix *rlGetMatrixProjectionStereo_(int eye);
 
 Matrix *rlGetMatrixViewOffsetStereo_(int eye);
diff --git a/raygui/src/raygui.h b/raygui/src/raygui.h
--- a/raygui/src/raygui.h
+++ b/raygui/src/raygui.h
@@ -1,6 +1,6 @@
 /*******************************************************************************************
 *
-*   raygui v4.1-dev - A simple and easy-to-use immediate-mode gui library
+*   raygui v4.5-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
@@ -141,8 +141,23 @@
 *           Draw text bounds rectangles for debug
 *
 *   VERSIONS HISTORY:
-*       4.1-dev (2024)    Current dev version...
+*       4.5-dev (Sep-2024)    Current dev version...
 *                         ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
+*                         ADDED: GuiValueBoxFloat()
+*                         ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
+*                         ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+*                         ADDED: Multiple new icons
+*                         REVIEWED: GuiTabBar(), close tab with mouse middle button
+*                         REVIEWED: GuiScrollPanel(), scroll speed proportional to content
+*                         REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
+*                         REVIEWED: GuiTextBox(), cursor position initialization
+*                         REVIEWED: GuiSliderPro(), control value change check
+*                         REVIEWED: GuiGrid(), simplified implementation
+*                         REVIEWED: GuiIconText(), increase buffer size and reviewed padding
+*                         REVIEWED: GuiDrawText(), improved wrap mode drawing
+*                         REVIEWED: GuiScrollBar(), minor tweaks
+*                         REVIEWED: Functions descriptions, removed wrong return value reference
+*                         REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
 *
 *       4.0 (12-Sep-2023) ADDED: GuiToggleSlider()
 *                         ADDED: GuiColorPickerHSV() and GuiColorPanelHSV()
@@ -317,9 +332,9 @@
 #define RAYGUI_H
 
 #define RAYGUI_VERSION_MAJOR 4
-#define RAYGUI_VERSION_MINOR 1
+#define RAYGUI_VERSION_MINOR 5
 #define RAYGUI_VERSION_PATCH 0
-#define RAYGUI_VERSION  "4.1-dev"
+#define RAYGUI_VERSION  "4.5-dev"
 
 #if !defined(RAYGUI_STANDALONE)
     #include "raylib.h"
@@ -617,7 +632,8 @@
 typedef enum {
     ARROW_PADDING = 16,         // DropdownBox arrow separation from border and items
     DROPDOWN_ITEMS_SPACING,     // DropdownBox items separation
-    DROPDOWN_ARROW_HIDDEN       // DropdownBox arrow hidden
+    DROPDOWN_ARROW_HIDDEN,      // DropdownBox arrow hidden
+    DROPDOWN_ROLL_UP            // DropdownBox roll up flag (default rolls down)
 } GuiDropdownBoxProperty;
 
 // TextBox/TextBoxMulti/ValueBox/Spinner
@@ -977,12 +993,12 @@
     ICON_WARNING                  = 220,
     ICON_HELP_BOX                 = 221,
     ICON_INFO_BOX                 = 222,
-    ICON_223                      = 223,
-    ICON_224                      = 224,
-    ICON_225                      = 225,
-    ICON_226                      = 226,
-    ICON_227                      = 227,
-    ICON_228                      = 228,
+    ICON_PRIORITY                 = 223,
+    ICON_LAYERS_ISO               = 224,
+    ICON_LAYERS2                  = 225,
+    ICON_MLAYERS                  = 226,
+    ICON_MAPS                     = 227,
+    ICON_HOT                      = 228,
     ICON_229                      = 229,
     ICON_230                      = 230,
     ICON_231                      = 231,
@@ -1029,6 +1045,7 @@
 
 #if defined(RAYGUI_IMPLEMENTATION)
 
+#include <ctype.h>              // required for: isspace() [GuiTextBox()]
 #include <stdio.h>              // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
 #include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
 #include <string.h>             // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy()
@@ -1300,7 +1317,7 @@
     0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0,      // ICON_LAYERS2
     0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0,      // ICON_MLAYERS
     0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0,      // ICON_MAPS
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_228
+    0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70,      // ICON_HOT
     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
@@ -1623,9 +1640,9 @@
 
     // Draw control
     //--------------------------------------------------------------------
-    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)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)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)? (int)BORDER_COLOR_DISABLED : (int)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)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
 
     GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text);
     //--------------------------------------------------------------------
@@ -1646,7 +1663,7 @@
     int result = 0;
     GuiState state = guiState;
 
-    Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR));
+    Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR));
 
     // Draw control
     //--------------------------------------------------------------------
@@ -1694,7 +1711,7 @@
     //--------------------------------------------------------------------
     if (text != NULL) GuiStatusBar(statusBar, text);  // Draw panel header as status bar
 
-    GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)),
+    GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED: (int)LINE_COLOR)),
                      GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)));
     //--------------------------------------------------------------------
 
@@ -2335,12 +2352,16 @@
     int itemSelected = *active;
     int itemFocused = -1;
 
+    int direction = 0; // Dropdown box open direction: down (default)
+    if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up
+
     // Get substrings items from text (items pointers, lengths and count)
     int itemCount = 0;
     const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
 
     Rectangle boundsOpen = bounds;
     boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+    if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING);
 
     Rectangle itemBounds = bounds;
 
@@ -2367,7 +2388,8 @@
             for (int i = 0; i < itemCount; i++)
             {
                 // Update item rectangle y position for next item
-                itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+                if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+                else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
 
                 if (CheckCollisionPointRec(mousePoint, itemBounds))
                 {
@@ -2411,7 +2433,8 @@
         for (int i = 0; i < itemCount; i++)
         {
             // Update item rectangle y position for next item
-            itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+            if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+            else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
 
             if (i == itemSelected)
             {
@@ -2434,7 +2457,7 @@
         GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
             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 },
+        GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 },
             TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));   // ICON_ARROW_DOWN_FILL
 #endif
     }
@@ -2581,24 +2604,59 @@
                 }
             }
 
-            // Delete codepoint from text, before current cursor position
-            if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+            // Delete related codepoints from text, before current cursor position
+            if ((textLength > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
             {
-                autoCursorDelayCounter++;
+                int i = textBoxCursorIndex - 1;
+                int accCodepointSize = 0;
 
-                if (IsKeyPressed(KEY_BACKSPACE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
+                // Move cursor to the end of word if on space already
+                while ((i > 0) && isspace(text[i]))
                 {
                     int prevCodepointSize = 0;
-                    GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+                    GetCodepointPrevious(text + i, &prevCodepointSize);
+                    i -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
+                }
 
-                    // Move backward text from cursor position
-                    for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
+                // Move cursor to the start of the word
+                while ((i > 0) && !isspace(text[i]))
+                {
+                    int prevCodepointSize = 0;
+                    GetCodepointPrevious(text + i, &prevCodepointSize);
+                    i -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
+                }
 
-                    // TODO Check: >= cursor+codepointsize and <= length-codepointsize
+                // Move forward text from cursor position
+                for (int j = (textBoxCursorIndex - accCodepointSize); j < textLength; j++) text[j] = text[j + accCodepointSize];
 
+                // Prevent cursor index from decrementing past 0
+                if (textBoxCursorIndex > 0)
+                {
+                    textBoxCursorIndex -= accCodepointSize;
+                    textLength -= accCodepointSize;
+                }
+
+                // Make sure text last character is EOL
+                text[textLength] = '\0';
+            } 
+            else 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;
+
                     // Prevent cursor index from decrementing past 0
                     if (textBoxCursorIndex > 0)
                     {
+                        GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+
+                        // Move backward text from cursor position
+                        for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
+
                         textBoxCursorIndex -= codepointSize;
                         textLength -= codepointSize;
                     }
@@ -2616,7 +2674,7 @@
                 if (IsKeyPressed(KEY_LEFT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
                 {
                     int prevCodepointSize = 0;
-                    GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+                    if (textBoxCursorIndex > 0) GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
 
                     if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize;
                 }
@@ -2903,7 +2961,7 @@
             //if (*value > maxValue) *value = maxValue;
             //else if (*value < minValue) *value = minValue;
 
-            if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
             {
                 if (*value > maxValue) *value = maxValue;
                 else if (*value < minValue) *value = minValue;
@@ -3019,7 +3077,7 @@
 
             if (valueHasChanged) *value = TextToFloat(textValue);
 
-            if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1;
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1;
         }
         else
         {
@@ -3404,7 +3462,7 @@
 
     // Draw control
     //--------------------------------------------------------------------
-    GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));     // Draw background
+    GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, 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++)
@@ -3859,7 +3917,7 @@
     buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
     buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
 
-    int textWidth = GetTextWidth(message) + 2;
+    //int textWidth = GetTextWidth(message) + 2;
 
     Rectangle textBounds = { 0 };
     textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
@@ -4463,7 +4521,7 @@
                 // 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);
+                if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int j = 1; j < RAYGUI_MAX_CONTROLS; j++) GuiSetStyle(j, (int)propertyId, propertyValue);
             }
             else GuiSetStyle((int)controlId, (int)propertyId, propertyValue);
         }
diff --git a/raygui/styles/amber/style_amber.h b/raygui/styles/amber/style_amber.h
--- a/raygui/styles/amber/style_amber.h
+++ b/raygui/styles/amber/style_amber.h
@@ -7,599 +7,596 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2023 raylib technologies (@raylibtech)                    //
-//                                                                              //
-//////////////////////////////////////////////////////////////////////////////////
-
-#define AMBER_STYLE_PROPS_COUNT  17
-
-// Custom style name: Amber
-static const GuiStyleProp amberStyleProps[AMBER_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0x898988ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x292929ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0xd4d4d4ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0xf1850dff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0x292929ff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0xffffffff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0xf1850dff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0xf1850dff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0xffffffff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x6a6a6aff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0x818181ff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x606060ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x00000010 },    // DEFAULT_TEXT_SIZE 
-    { 0, 18, 0xf1850dff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0x333333ff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
-    { 10, 7, 0x202020ff },    // VALUEBOX_BASE_COLOR_PRESSED 
-};
-
-// WARNING: This style uses a custom font: "Inter-Regular.ttf" (size: 16, spacing: 1)
-
-#define AMBER_STYLE_FONT_ATLAS_COMP_SIZE 6417
-
-// Font atlas image pixels data: DEFLATE compressed
-static unsigned char amberFontData[AMBER_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
-    0x9d, 0x07, 0x78, 0x15, 0xc5, 0xda, 0xc7, 0x4f, 0x1a, 0x3d, 0x20, 0x4d, 0x10, 0x41, 0x45, 0xa5, 0x89, 0x8a, 0x7a, 0x45,
-    0xb0, 0x60, 0x47, 0x51, 0xb0, 0xe3, 0xc5, 0x02, 0x36, 0x14, 0x95, 0x0b, 0x28, 0x88, 0x5c, 0xf1, 0x43, 0x6c, 0x20, 0xd2,
-    0x14, 0x15, 0xb1, 0x80, 0xc1, 0x02, 0xd8, 0x50, 0x14, 0x51, 0x41, 0x91, 0x2e, 0x1d, 0x2e, 0xc5, 0x84, 0x2e, 0xed, 0x86,
-    0x16, 0x02, 0x84, 0x10, 0x02, 0x21, 0xe4, 0xf7, 0x3d, 0x3b, 0x67, 0xcf, 0xc9, 0x81, 0xf3, 0xce, 0x9e, 0x9c, 0x18, 0x10,
-    0xb8, 0x2f, 0xbf, 0xe7, 0x21, 0xc9, 0xce, 0xd6, 0xf9, 0xcf, 0xce, 0xcc, 0xee, 0xfe, 0xdf, 0x19, 0x7c, 0x8a, 0xa2, 0x28,
-    0x8a, 0xa2, 0x28, 0x87, 0xf0, 0x1c, 0xf7, 0x84, 0x2d, 0x7b, 0x94, 0x07, 0x68, 0xca, 0x0b, 0xe2, 0xfa, 0x75, 0xe9, 0x62,
-    0x7e, 0xf6, 0xe3, 0x24, 0xeb, 0x3e, 0x63, 0x48, 0xb0, 0xa4, 0xc4, 0xd2, 0x93, 0x71, 0xb4, 0x72, 0xff, 0x2a, 0xc1, 0x3b,
-    0x54, 0xb0, 0xac, 0xd9, 0x84, 0x21, 0x0c, 0xe4, 0x74, 0x4b, 0xaa, 0x73, 0x86, 0xd2, 0xf2, 0x8b, 0x29, 0x19, 0xb6, 0xac,
-    0x02, 0xe7, 0x58, 0xcf, 0xf4, 0x64, 0xba, 0x5b, 0xd3, 0xea, 0xf0, 0x1f, 0x6b, 0x5a, 0x23, 0x2e, 0x0f, 0x5b, 0x56, 0x86,
-    0x11, 0x2e, 0xa7, 0x84, 0xa5, 0x75, 0xa0, 0x85, 0x65, 0x4f, 0xa5, 0x29, 0x21, 0x2e, 0xbf, 0x84, 0xf7, 0xf9, 0x8a, 0xb6,
-    0xd6, 0x7c, 0x7c, 0x8a, 0x24, 0xce, 0x2c, 0x02, 0xfd, 0x27, 0xd1, 0x2f, 0x6c, 0xd9, 0x07, 0x0c, 0xe2, 0x71, 0xa6, 0x89,
-    0xeb, 0xb7, 0x65, 0x08, 0x3e, 0x8a, 0x91, 0x41, 0xac, 0x75, 0x9f, 0xb5, 0xf8, 0xc4, 0x92, 0x72, 0x2f, 0x13, 0xb9, 0x90,
-    0x54, 0x4e, 0x35, 0x7f, 0x0d, 0x10, 0x8e, 0x1d, 0x28, 0x65, 0x99, 0x74, 0x64, 0x28, 0x6b, 0x88, 0x11, 0xd3, 0x9d, 0x33,
-    0x0c, 0x5f, 0x7a, 0x35, 0xa9, 0xc4, 0x87, 0x2d, 0x3d, 0x95, 0x3d, 0x54, 0x13, 0xd6, 0x2e, 0xce, 0xd3, 0xd4, 0xe4, 0x57,
-    0x6a, 0xd2, 0x5a, 0x3c, 0xc6, 0xb9, 0xa4, 0x59, 0xaf, 0x70, 0x22, 0x3d, 0xc3, 0x96, 0x95, 0xa0, 0xa3, 0x4b, 0xf9, 0xb0,
-    0xb4, 0x05, 0x0c, 0xb5, 0xec, 0x69, 0x2a, 0xb7, 0x8b, 0xe5, 0x2b, 0x8b, 0x89, 0xf4, 0x63, 0x3f, 0xbd, 0x85, 0xd4, 0xca,
-    0x8c, 0x67, 0x11, 0x79, 0x34, 0x2a, 0x02, 0xfd, 0x7f, 0xa5, 0x4f, 0xd8, 0xb2, 0x37, 0x79, 0x8d, 0x36, 0x8c, 0x17, 0xd7,
-    0x1f, 0x40, 0x07, 0x93, 0x37, 0x0b, 0xad, 0x7b, 0x6c, 0xca, 0xe7, 0x6c, 0xe0, 0x15, 0xf1, 0xde, 0x7d, 0xc9, 0xd4, 0x2a,
-    0x93, 0x69, 0x82, 0x8f, 0xab, 0x58, 0x44, 0x71, 0xcb, 0x3e, 0xfe, 0xcd, 0x38, 0x7c, 0x54, 0x23, 0xcf, 0x52, 0x3f, 0x38,
-    0x67, 0x18, 0xbe, 0xf4, 0x07, 0x5e, 0x16, 0xd7, 0xfe, 0x56, 0x5c, 0x3b, 0x91, 0xfe, 0x2c, 0x61, 0x33, 0x0b, 0x69, 0x19,
-    0x96, 0xd6, 0x90, 0x66, 0x3c, 0xce, 0x2e, 0x9a, 0xd1, 0x8c, 0xb3, 0x84, 0x6d, 0x37, 0xd2, 0x38, 0xaa, 0x5c, 0x8e, 0xb7,
-    0x94, 0xe3, 0x32, 0xec, 0xa5, 0x9c, 0xb0, 0x7c, 0x20, 0xef, 0x99, 0x2d, 0xae, 0xe2, 0xbf, 0x42, 0xea, 0xc3, 0x0c, 0xa1,
-    0x04, 0x07, 0x8b, 0x44, 0xff, 0xf1, 0xf4, 0x0a, 0x5b, 0xd6, 0x9b, 0x1e, 0xb4, 0xe4, 0xdb, 0xb0, 0xe5, 0x67, 0x90, 0x4d,
-    0x2e, 0xfb, 0xc9, 0x66, 0x3f, 0x07, 0xc9, 0x16, 0xca, 0xb9, 0x8f, 0x27, 0x49, 0x67, 0x18, 0xb3, 0xf9, 0x8e, 0x2d, 0x42,
-    0xfd, 0xd4, 0x9c, 0xf9, 0xb4, 0x62, 0x13, 0x15, 0x48, 0x64, 0x05, 0x17, 0x58, 0xcf, 0xea, 0x5a, 0x76, 0x70, 0x36, 0xdf,
-    0x58, 0xca, 0xa0, 0xff, 0x0c, 0x0f, 0x5f, 0x56, 0x8a, 0x7d, 0x34, 0x10, 0xd7, 0x6e, 0x4d, 0xb2, 0xb0, 0x34, 0x8e, 0xd6,
-    0x2c, 0x25, 0x93, 0xef, 0x39, 0x4f, 0xc8, 0xff, 0x49, 0xcc, 0x21, 0x87, 0x49, 0x4c, 0xa2, 0x8d, 0x50, 0x72, 0xd2, 0x89,
-    0x2b, 0x92, 0xd6, 0xb7, 0x39, 0x33, 0xc5, 0xe5, 0xf3, 0xb8, 0xcd, 0x3d, 0xc7, 0x3d, 0x9c, 0x2d, 0x5c, 0xa9, 0xf3, 0x7f,
-    0xd1, 0xe8, 0xff, 0x23, 0x2f, 0x85, 0x2d, 0x4b, 0x20, 0x81, 0x38, 0xe1, 0xde, 0x8c, 0x21, 0x9e, 0xf5, 0x54, 0x27, 0x9e,
-    0x57, 0xe8, 0x21, 0xd4, 0xb4, 0x0e, 0x2b, 0x78, 0xd4, 0xad, 0xff, 0xbf, 0xe0, 0x61, 0x51, 0x8b, 0x41, 0xa6, 0xed, 0x1c,
-    0xc6, 0xf3, 0x9e, 0xe7, 0xf5, 0x2d, 0x39, 0x4c, 0xa0, 0xac, 0x25, 0x35, 0x41, 0xe8, 0x61, 0xdc, 0x48, 0x9a, 0xa5, 0x4d,
-    0x3a, 0x95, 0x3c, 0xa1, 0x05, 0xa8, 0xc0, 0x50, 0x2e, 0xe0, 0x17, 0xae, 0xb5, 0xd4, 0x1a, 0xf6, 0xfa, 0xbf, 0x0c, 0x57,
-    0x16, 0x51, 0xef, 0x6b, 0x90, 0x78, 0xec, 0xb2, 0xe4, 0x52, 0xdd, 0xfd, 0x7d, 0x06, 0x8f, 0x58, 0xb6, 0x2d, 0x1a, 0xfd,
-    0xc7, 0x5a, 0xfa, 0x79, 0xb6, 0x2b, 0x4f, 0x37, 0x3f, 0xbf, 0xe2, 0x0e, 0x6b, 0x7b, 0xd6, 0xc3, 0xa3, 0xfd, 0x0f, 0x70,
-    0x0b, 0x33, 0x89, 0x23, 0x96, 0x3a, 0x96, 0x16, 0xe0, 0x16, 0x32, 0x58, 0xcb, 0x87, 0xf8, 0x38, 0x85, 0xf3, 0x0b, 0x78,
-    0x6e, 0x4f, 0xf3, 0x9b, 0x35, 0x6d, 0x1b, 0xd7, 0x59, 0x4a, 0xc6, 0x87, 0xd6, 0x6d, 0xbc, 0xda, 0xff, 0xa2, 0x22, 0xd9,
-    0xb4, 0x84, 0x87, 0x53, 0x93, 0x3c, 0x4a, 0x07, 0xf5, 0x79, 0xee, 0x88, 0xea, 0x3f, 0x9c, 0xa7, 0x0a, 0xbc, 0x6e, 0x7b,
-    0xbe, 0x65, 0x07, 0xc3, 0x19, 0xce, 0x36, 0xc6, 0x72, 0xb7, 0xb8, 0xce, 0x75, 0x6c, 0x65, 0x3e, 0x53, 0xac, 0x3d, 0x77,
-    0x87, 0x4a, 0xac, 0xa1, 0x16, 0x09, 0xcc, 0x60, 0x12, 0x6b, 0x85, 0x35, 0xab, 0xb0, 0x87, 0x7b, 0xa9, 0xce, 0x26, 0x3a,
-    0xf2, 0x1c, 0x1f, 0x17, 0xf0, 0xec, 0xfa, 0x32, 0xd2, 0x9a, 0xb6, 0xc4, 0xd2, 0xc7, 0xf3, 0xa2, 0x1c, 0x77, 0x1d, 0x61,
-    0xf5, 0x4f, 0x65, 0xb7, 0xe5, 0x49, 0xe9, 0xbf, 0xc1, 0xe7, 0x8b, 0x8d, 0x62, 0x09, 0x29, 0x3a, 0xfd, 0xa3, 0xe1, 0x5a,
-    0x86, 0x32, 0x9e, 0x7b, 0x68, 0x43, 0x36, 0xf7, 0x50, 0xd7, 0x5a, 0x47, 0xbc, 0xcc, 0x6e, 0x0e, 0xf0, 0x99, 0xb5, 0x77,
-    0x37, 0x9a, 0x7f, 0x99, 0x27, 0x9c, 0x39, 0xf8, 0x78, 0x83, 0xce, 0x61, 0xe9, 0xd7, 0xb3, 0xdb, 0xd4, 0xe4, 0x97, 0xb0,
-    0x9b, 0xf5, 0x3c, 0x5e, 0xc0, 0xb3, 0xeb, 0xc3, 0x28, 0x6b, 0xda, 0x52, 0xee, 0x3b, 0x06, 0x9f, 0xbd, 0x1f, 0x64, 0xac,
-    0x25, 0x65, 0x94, 0x7b, 0x57, 0x56, 0x25, 0xdb, 0x9a, 0x8b, 0x45, 0xa3, 0x7f, 0x7b, 0x9a, 0x47, 0xb1, 0xf6, 0xeb, 0x3c,
-    0x8d, 0x8f, 0x7a, 0x2c, 0xf1, 0x5c, 0xcb, 0xa9, 0xff, 0xcf, 0x24, 0x99, 0x17, 0x2d, 0xbd, 0xb1, 0x09, 0xa6, 0x6f, 0xdb,
-    0x90, 0xb9, 0xa6, 0x05, 0xec, 0x14, 0xb6, 0xc6, 0x49, 0xec, 0xa3, 0x1d, 0x09, 0x34, 0x66, 0x1e, 0x50, 0xbf, 0x80, 0xe7,
-    0xf6, 0x08, 0xf3, 0xac, 0x4f, 0xcb, 0x7b, 0xb9, 0xf8, 0x18, 0xd4, 0x7f, 0x84, 0x70, 0xed, 0x7e, 0xda, 0xb1, 0x95, 0xd6,
-    0x9c, 0xcb, 0x74, 0x8f, 0x36, 0xad, 0x68, 0xf4, 0xb7, 0x3f, 0x99, 0xca, 0xbd, 0x85, 0x1b, 0xf1, 0x71, 0x17, 0x5f, 0x58,
-    0xd2, 0xcb, 0xd3, 0x8b, 0x8a, 0x6e, 0xfb, 0x3f, 0x98, 0xaf, 0x84, 0x35, 0xaa, 0xf3, 0xa7, 0xdb, 0xb7, 0x49, 0x60, 0x0a,
-    0xb3, 0x59, 0xe3, 0xbe, 0x0b, 0x38, 0xbc, 0x06, 0x58, 0x45, 0x16, 0x39, 0x8c, 0xe4, 0x0d, 0x52, 0xc4, 0x27, 0x24, 0xe9,
-    0x8d, 0x91, 0xed, 0x9d, 0xc4, 0x99, 0x60, 0xed, 0x49, 0xfe, 0x7d, 0xc4, 0xb0, 0x99, 0x7a, 0x96, 0xb4, 0x38, 0x06, 0x91,
-    0x45, 0x2e, 0x33, 0x83, 0xfd, 0xc0, 0x23, 0xa5, 0xbf, 0xed, 0xc9, 0xb4, 0xb0, 0x4c, 0x67, 0x04, 0x4d, 0xf8, 0x8c, 0xe6,
-    0xec, 0xa2, 0x5d, 0xc4, 0xb5, 0x4f, 0xb7, 0x3c, 0x45, 0xf8, 0xcb, 0x52, 0xb1, 0xa8, 0x8e, 0x5c, 0x9c, 0xcd, 0xdc, 0x64,
-    0x79, 0xc7, 0x39, 0xe5, 0x18, 0xbc, 0xfb, 0xcf, 0x67, 0xa3, 0x67, 0x7a, 0xc2, 0x31, 0x58, 0x66, 0x23, 0x73, 0x1e, 0x5f,
-    0x90, 0x45, 0x1e, 0xdb, 0x78, 0xa9, 0x88, 0x4b, 0x56, 0x64, 0x7a, 0x0a, 0xef, 0x2c, 0x9c, 0xfb, 0x6c, 0x95, 0xf5, 0x69,
-    0xe5, 0xef, 0xa4, 0x0b, 0xc3, 0x4f, 0xd0, 0x6f, 0x0a, 0xe7, 0xf1, 0xb5, 0xc7, 0xdb, 0xe1, 0x23, 0xc7, 0xc9, 0xcc, 0x24,
-    0x51, 0x38, 0x9b, 0x5f, 0x8a, 0xe8, 0x6d, 0x4d, 0xd1, 0x72, 0x13, 0x17, 0x9e, 0xa0, 0xfa, 0x97, 0xe3, 0x1a, 0xfd, 0xb2,
-    0xa6, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0x72, 0x54, 0xb9, 0x8d, 0x8f,
-    0xf9, 0xd4, 0xc3, 0xd7, 0x72, 0x2b, 0x1f, 0xf1, 0x25, 0x8f, 0x79, 0x7e, 0xb9, 0xa9, 0x49, 0x92, 0xe8, 0x19, 0x68, 0x40,
-    0x92, 0x4b, 0x4f, 0xcb, 0x96, 0xb5, 0x79, 0x83, 0x31, 0x3c, 0x27, 0x3a, 0xde, 0x2f, 0x0b, 0x6e, 0x9d, 0x24, 0x38, 0x92,
-    0xfd, 0x9e, 0xb0, 0x24, 0x3e, 0xe7, 0x11, 0xeb, 0xb9, 0xb5, 0x34, 0xe7, 0xde, 0x52, 0x4c, 0xfb, 0x84, 0x6e, 0xc2, 0xd2,
-    0xee, 0x24, 0x79, 0x7e, 0x7f, 0x2b, 0x2d, 0x38, 0x6f, 0x7f, 0x08, 0xd2, 0x50, 0x78, 0xef, 0xed, 0x9d, 0xfb, 0x67, 0x31,
-    0x88, 0x31, 0x74, 0xb7, 0x3a, 0x3a, 0x02, 0xfe, 0x99, 0xcb, 0x38, 0x2d, 0xe2, 0x17, 0x96, 0x24, 0x2e, 0x8b, 0x5a, 0xfd,
-    0x27, 0xd8, 0xc2, 0x8b, 0xbc, 0xc0, 0x66, 0x3a, 0x8a, 0xe9, 0x9d, 0xd9, 0x4c, 0x67, 0x9e, 0xe1, 0x4f, 0xfa, 0x7b, 0x3a,
-    0x86, 0x0f, 0x8a, 0x51, 0x13, 0x8f, 0xb1, 0x8a, 0x67, 0x0d, 0x6d, 0x2c, 0x9e, 0xfe, 0x6d, 0xcc, 0xa6, 0x33, 0x0b, 0x99,
-    0x28, 0xa4, 0xd6, 0x77, 0xb7, 0x7d, 0x96, 0xc9, 0xc6, 0x17, 0x12, 0x9e, 0xf3, 0xdb, 0x79, 0xd9, 0x9c, 0x9b, 0x1c, 0x33,
-    0xd0, 0x8d, 0x74, 0x9e, 0xa7, 0x1b, 0x9b, 0x44, 0xbf, 0xdc, 0x30, 0x37, 0x6a, 0xe5, 0x50, 0x9e, 0xe5, 0x03, 0xeb, 0x57,
-    0xf1, 0x87, 0x58, 0x2d, 0x78, 0xbc, 0xff, 0xc1, 0x03, 0x41, 0xaa, 0x0b, 0xdf, 0x99, 0xa7, 0x72, 0xb5, 0xc7, 0xd7, 0xa9,
-    0x34, 0x3e, 0xe6, 0x21, 0xe6, 0x32, 0xda, 0xea, 0x08, 0x78, 0x81, 0x85, 0xac, 0xe7, 0x07, 0x16, 0xb1, 0x99, 0x6e, 0x1e,
-    0x5f, 0xad, 0xde, 0x61, 0x1d, 0x5f, 0x46, 0xad, 0xff, 0x62, 0xe3, 0xdd, 0x77, 0xdc, 0x25, 0x29, 0x16, 0xcf, 0xed, 0x0d,
-    0xe6, 0xe7, 0xdd, 0xa4, 0x5b, 0xef, 0xb2, 0xfb, 0x49, 0xe6, 0x67, 0x51, 0xff, 0x5e, 0x1e, 0xfe, 0x3b, 0xff, 0x3d, 0xf8,
-    0xbe, 0xd9, 0x6b, 0x55, 0x66, 0x0b, 0x9e, 0xe6, 0xfc, 0x28, 0x8a, 0x8d, 0xdc, 0x2f, 0xfa, 0x0a, 0x7a, 0xba, 0x77, 0xf9,
-    0x76, 0xe1, 0xdc, 0xe2, 0xc9, 0x74, 0xbf, 0xfd, 0x37, 0x62, 0x8f, 0xe8, 0x4b, 0x2f, 0x38, 0x71, 0x3c, 0xc0, 0x2a, 0xfe,
-    0xa0, 0xb5, 0x87, 0x37, 0x41, 0xe6, 0x24, 0xfe, 0x8f, 0x6d, 0x4c, 0xb6, 0xb8, 0x83, 0x9b, 0x31, 0xd1, 0xec, 0xb1, 0x11,
-    0x39, 0x42, 0x94, 0x92, 0xa3, 0xfe, 0x3b, 0x4c, 0x0d, 0x46, 0x1b, 0x54, 0x66, 0x8c, 0xd5, 0x1d, 0x96, 0x40, 0x1a, 0x77,
-    0xb0, 0xd7, 0x23, 0x02, 0xcb, 0xe6, 0x37, 0x2c, 0xe6, 0xe6, 0xe1, 0x4a, 0xcf, 0xf5, 0x9e, 0xb2, 0xfa, 0xbb, 0x2a, 0xb2,
-    0x85, 0x2b, 0xf8, 0x5e, 0xd4, 0xff, 0x53, 0xfa, 0x50, 0x9f, 0x8b, 0x2d, 0xa5, 0x36, 0x8e, 0x6c, 0x5a, 0x50, 0x81, 0xc6,
-    0x96, 0x78, 0xa7, 0x00, 0x1d, 0x59, 0x26, 0xee, 0xe1, 0x73, 0xde, 0x71, 0xe3, 0x0b, 0x16, 0x8b, 0x2d, 0x0b, 0xee, 0x97,
-    0xdf, 0x58, 0xb2, 0xb9, 0xe2, 0x2f, 0xa8, 0x7f, 0x0f, 0x2b, 0x98, 0xcd, 0x6d, 0x96, 0xf2, 0xdf, 0x83, 0xd4, 0x20, 0xd7,
-    0x8b, 0x6b, 0x94, 0xa2, 0x13, 0xeb, 0x99, 0xe8, 0xe1, 0x37, 0xbb, 0x97, 0xad, 0xe2, 0xde, 0xbb, 0x32, 0x8f, 0xb2, 0x24,
-    0xd0, 0x94, 0x27, 0x39, 0x9b, 0xdb, 0x88, 0x65, 0xb6, 0x78, 0x27, 0x38, 0xad, 0xf4, 0x3a, 0x62, 0x58, 0xc8, 0x63, 0x85,
-    0xba, 0xc2, 0x92, 0x2c, 0xe6, 0x19, 0x6b, 0xea, 0xa3, 0x8c, 0x64, 0x05, 0xd7, 0x5a, 0x52, 0x93, 0x78, 0x1f, 0x9f, 0x45,
-    0xff, 0x29, 0xa4, 0xb1, 0x92, 0x6d, 0x6c, 0x15, 0xe3, 0x3a, 0x9c, 0x58, 0x9e, 0xde, 0x64, 0xf0, 0x27, 0x59, 0xd6, 0x58,
-    0x38, 0xe7, 0xdc, 0x52, 0x2d, 0x4e, 0xcd, 0x3a, 0xcc, 0xe1, 0x5b, 0x86, 0xb3, 0x4c, 0xbc, 0xbb, 0xe2, 0xc9, 0x70, 0x23,
-    0x26, 0x6e, 0x04, 0x4b, 0xfb, 0x53, 0x30, 0x26, 0xb3, 0x89, 0x5b, 0xac, 0xb5, 0x5f, 0x55, 0xce, 0x0d, 0x92, 0x68, 0xbd,
-    0x86, 0xce, 0xe4, 0x08, 0x51, 0x15, 0x01, 0xf5, 0x77, 0xf3, 0x90, 0xe5, 0xc8, 0x4e, 0xa9, 0x1b, 0xc5, 0x34, 0xde, 0x67,
-    0x0d, 0xbb, 0xf0, 0xd1, 0x42, 0x6c, 0x29, 0x1d, 0xf7, 0xfd, 0x00, 0xd3, 0xda, 0x4c, 0x2f, 0xc4, 0xf5, 0xc5, 0x32, 0x9a,
-    0x09, 0x1e, 0x35, 0xdb, 0xab, 0xfc, 0xce, 0x2a, 0x8b, 0xb7, 0xfb, 0x1a, 0x52, 0x4d, 0x9d, 0x23, 0xeb, 0x5f, 0xcf, 0x38,
-    0x35, 0x63, 0x79, 0x83, 0x75, 0x62, 0xef, 0x10, 0x26, 0x98, 0x7b, 0xbf, 0x35, 0xd9, 0xd6, 0xa8, 0xcf, 0xa7, 0x48, 0xb1,
-    0xd4, 0x1f, 0xb7, 0xb0, 0x81, 0x29, 0x8c, 0x64, 0xbd, 0xdb, 0x86, 0x85, 0x6f, 0x99, 0x41, 0x7f, 0x06, 0x92, 0x4c, 0x6a,
-    0x30, 0xbe, 0x34, 0x52, 0xfc, 0xa5, 0x14, 0xc5, 0x19, 0xcb, 0x3f, 0xf9, 0x83, 0x25, 0xdc, 0x17, 0x75, 0xed, 0xef, 0xef,
-    0x01, 0x3e, 0xc7, 0x16, 0x7e, 0xe2, 0x12, 0x4b, 0xfa, 0xa5, 0xec, 0xa3, 0x99, 0xa5, 0xc7, 0xe1, 0x44, 0x29, 0x5e, 0xcc,
-    0x12, 0x73, 0xdc, 0x3b, 0x8c, 0xfe, 0xa7, 0xb2, 0x4d, 0x6c, 0x65, 0xb2, 0x4d, 0xed, 0x72, 0x1a, 0x07, 0xc5, 0xd8, 0x34,
-    0x6f, 0xde, 0x63, 0x71, 0x44, 0x5f, 0xd9, 0x55, 0xe4, 0x52, 0x55, 0x68, 0x99, 0x57, 0xd0, 0x86, 0x92, 0x94, 0xe4, 0x07,
-    0x7a, 0x7a, 0xf4, 0x61, 0xcb, 0x73, 0x90, 0x5a, 0x42, 0xef, 0x87, 0x60, 0xbd, 0xbc, 0xd6, 0xbd, 0x57, 0xc3, 0xef, 0x9c,
-    0xcd, 0x42, 0x34, 0xb2, 0xff, 0xd8, 0xbb, 0xdd, 0xda, 0xee, 0x5c, 0x72, 0x2d, 0xae, 0xc9, 0x1b, 0x79, 0xcb, 0xc4, 0x9e,
-    0xec, 0x13, 0xea, 0x7f, 0xd9, 0xe5, 0x3a, 0xd1, 0xf2, 0xa4, 0x12, 0xcb, 0x3d, 0xa4, 0xb0, 0x9a, 0x9a, 0x51, 0xe6, 0xed,
-    0xad, 0xec, 0x64, 0xbc, 0x67, 0x64, 0x60, 0x37, 0x7e, 0xb4, 0xa4, 0xd4, 0x33, 0x5a, 0x3f, 0xe2, 0x9e, 0x67, 0x35, 0x57,
-    0xff, 0x4c, 0xb1, 0x86, 0xce, 0x63, 0x1a, 0x53, 0x99, 0xca, 0x3e, 0x6b, 0x2d, 0x63, 0xa3, 0x17, 0x2b, 0x05, 0x65, 0x03,
-    0x51, 0x6d, 0x1d, 0xa9, 0xe8, 0xf6, 0x2f, 0x76, 0x09, 0xf7, 0xcb, 0xa5, 0x84, 0xfe, 0x9b, 0x25, 0x44, 0x06, 0x9f, 0xe5,
-    0x2a, 0x9d, 0x27, 0x7a, 0x7a, 0x77, 0xb9, 0xbd, 0xcb, 0x18, 0xd2, 0x2c, 0xd1, 0x38, 0x5d, 0xf8, 0xc3, 0xe2, 0x1b, 0xab,
-    0x05, 0xd4, 0x70, 0xef, 0x93, 0x74, 0xee, 0x0c, 0x4b, 0x2f, 0x46, 0x17, 0xaa, 0x04, 0x5d, 0xc0, 0x89, 0x05, 0x74, 0xb9,
-    0x7a, 0x45, 0x71, 0xc6, 0x71, 0x9f, 0x50, 0x8a, 0xbd, 0x69, 0x16, 0xf1, 0x99, 0xac, 0xac, 0xb5, 0x6f, 0x5a, 0x89, 0xfd,
-    0xc4, 0x73, 0x3d, 0x93, 0xdc, 0xb2, 0x9c, 0x61, 0x1c, 0xf2, 0x2b, 0xc4, 0x08, 0xab, 0xe1, 0xdc, 0x6e, 0x18, 0xc0, 0xea,
-    0xa8, 0x3c, 0x96, 0x4f, 0x93, 0xc6, 0x3f, 0xa8, 0x64, 0x88, 0x15, 0xfa, 0x9f, 0x6b, 0x18, 0x45, 0x0d, 0xaa, 0xd0, 0x87,
-    0x2c, 0xa1, 0x6f, 0x19, 0x47, 0x69, 0x97, 0x9f, 0x78, 0x51, 0xe8, 0xc1, 0x7e, 0xcd, 0x64, 0x6a, 0x52, 0x89, 0x51, 0xcc,
-    0x16, 0x8f, 0xfe, 0x32, 0x8b, 0xa9, 0x45, 0x39, 0x06, 0x92, 0xea, 0x46, 0x30, 0x1e, 0xde, 0x73, 0xda, 0xc2, 0x3f, 0xad,
-    0x4f, 0x46, 0xeb, 0xf9, 0x88, 0x1a, 0x54, 0xa4, 0x27, 0xfb, 0xa9, 0x24, 0x3e, 0x1f, 0x8c, 0xa6, 0x1a, 0x37, 0x93, 0xcc,
-    0xc0, 0x02, 0xe6, 0x46, 0xd1, 0x45, 0x71, 0x46, 0xf3, 0xfe, 0xc5, 0xee, 0x89, 0xde, 0x4e, 0x5d, 0x4a, 0xb3, 0x82, 0x97,
-    0x68, 0xc3, 0x34, 0x72, 0x68, 0xc1, 0x68, 0xde, 0x12, 0x62, 0x70, 0xf3, 0x82, 0xf5, 0x5f, 0x79, 0xb1, 0xae, 0xb3, 0xfb,
-    0xcd, 0xf3, 0x42, 0xee, 0xdf, 0xd3, 0xc5, 0xbb, 0x6c, 0x06, 0x39, 0x1c, 0x60, 0xb1, 0xb5, 0xfd, 0x0a, 0x44, 0x01, 0x74,
-    0x17, 0x9f, 0x0d, 0xc6, 0x99, 0xd8, 0xe0, 0x5f, 0xa8, 0x6c, 0xa9, 0xdd, 0xdf, 0x27, 0x87, 0xfd, 0xcc, 0xb1, 0xd4, 0xdf,
-    0x5d, 0x59, 0xea, 0x39, 0xa6, 0xc0, 0xef, 0xe4, 0x90, 0x43, 0xb2, 0xe5, 0x8a, 0x4f, 0x65, 0x1a, 0x07, 0xd8, 0xcb, 0x87,
-    0xd6, 0xb1, 0x27, 0x8e, 0x5c, 0x14, 0x67, 0xc1, 0xf9, 0xd4, 0x12, 0xef, 0xeb, 0x1f, 0xd3, 0xe0, 0x13, 0x73, 0x9d, 0x83,
-    0xf8, 0x92, 0xab, 0x78, 0x84, 0xa9, 0x0c, 0x11, 0x5a, 0xea, 0xff, 0x3b, 0x64, 0x54, 0x8a, 0x6f, 0x3d, 0xe2, 0x17, 0x0b,
-    0x47, 0x09, 0x6b, 0xbf, 0xb6, 0x60, 0x5b, 0x97, 0x8a, 0xe0, 0xd1, 0x4f, 0x3c, 0x82, 0xe7, 0x96, 0x18, 0x65, 0xc4, 0xc0,
-    0xdf, 0x11, 0xf3, 0x61, 0xaf, 0x71, 0x2a, 0xb0, 0x95, 0x67, 0x8e, 0x49, 0xa7, 0xb2, 0x72, 0x74, 0xa8, 0xc5, 0xaf, 0x2c,
-    0x65, 0x30, 0x4f, 0xf1, 0x2c, 0x5f, 0x58, 0x9e, 0xc1, 0x94, 0x13, 0x9b, 0x7f, 0xd0, 0x9a, 0x97, 0x79, 0x81, 0x3b, 0x8e,
-    0xcb, 0xe8, 0x1f, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0xe5, 0xe8,
-    0xd3, 0xdb, 0x78, 0xeb, 0x3f, 0xa0, 0xa3, 0xe8, 0xce, 0xf0, 0x7f, 0xa1, 0xbd, 0x8b, 0x0f, 0xf8, 0x9c, 0xa7, 0xc4, 0xaf,
-    0x78, 0xfe, 0xed, 0xdf, 0xa4, 0xa5, 0xeb, 0xb4, 0x90, 0xd3, 0xfd, 0xc4, 0x7b, 0xa4, 0x0e, 0xb5, 0xf8, 0x26, 0xef, 0x66,
-    0x28, 0x23, 0xe8, 0x20, 0x7a, 0x63, 0x9d, 0xed, 0xaf, 0x0c, 0xf1, 0xd0, 0x3d, 0xe9, 0x99, 0x2e, 0x6d, 0xdd, 0xc4, 0x63,
-    0x8b, 0x26, 0x24, 0x09, 0x8e, 0x64, 0xaf, 0x3d, 0xf6, 0x66, 0x68, 0x88, 0x03, 0xaa, 0x2c, 0x49, 0xc2, 0x78, 0xd4, 0xbd,
-    0x43, 0xdc, 0x39, 0xd7, 0x59, 0xc6, 0x42, 0x74, 0x72, 0xfc, 0x7d, 0x3e, 0xe6, 0x7e, 0x4b, 0xec, 0x80, 0x3f, 0xd7, 0x86,
-    0xd0, 0xd6, 0xea, 0xda, 0x89, 0xb4, 0x87, 0x7c, 0x52, 0x18, 0x4d, 0x37, 0x5e, 0xe4, 0x77, 0x36, 0x8a, 0x25, 0x20, 0x9e,
-    0x71, 0xac, 0xa6, 0x1f, 0xdd, 0x59, 0xc0, 0x22, 0x61, 0x0e, 0x03, 0xff, 0xf6, 0x43, 0xd8, 0x49, 0x96, 0xf8, 0x0d, 0xde,
-    0x9f, 0xee, 0x27, 0xce, 0x23, 0xb5, 0xab, 0xe8, 0x68, 0x1e, 0xcf, 0x2a, 0xfa, 0xd2, 0x97, 0xd5, 0x2c, 0x10, 0x8e, 0xed,
-    0x6c, 0x9f, 0x3f, 0x17, 0xc2, 0x9b, 0xc2, 0xc8, 0x82, 0x29, 0xd6, 0xb9, 0x12, 0xfc, 0xa9, 0x8f, 0x58, 0xb7, 0xb8, 0x90,
-    0x5d, 0x62, 0x74, 0x88, 0xd7, 0x1e, 0x53, 0xc8, 0x0d, 0x71, 0x19, 0xb6, 0xe3, 0x80, 0xe0, 0x79, 0x49, 0x21, 0x2f, 0xe8,
-    0x54, 0xea, 0xc8, 0x7c, 0x31, 0xc7, 0x7f, 0x60, 0x35, 0xfd, 0x79, 0x85, 0xa5, 0xcc, 0x11, 0xa2, 0x4d, 0x9c, 0x7d, 0x7c,
-    0xc5, 0xb3, 0xf4, 0x66, 0x19, 0x6b, 0xc5, 0x98, 0x90, 0xc8, 0x7b, 0x08, 0xbf, 0x9e, 0x18, 0x92, 0xcd, 0xf8, 0xab, 0x87,
-    0xd3, 0x9f, 0xe5, 0xae, 0x2f, 0x33, 0x81, 0x49, 0xc6, 0x61, 0x2a, 0x6f, 0x5f, 0x92, 0xae, 0xec, 0x14, 0xbc, 0x71, 0x91,
-    0x14, 0xf0, 0x4a, 0x1d, 0xc8, 0x32, 0xd7, 0x19, 0x55, 0x8a, 0x95, 0xbc, 0x7d, 0x14, 0xf5, 0xaf, 0xcd, 0x56, 0x4b, 0xbc,
-    0x91, 0xb7, 0xfe, 0x93, 0xf9, 0x35, 0xf8, 0xd7, 0x6c, 0x26, 0x8b, 0xfa, 0x7f, 0x49, 0xaa, 0x7b, 0x57, 0xca, 0xfa, 0xf7,
-    0x0d, 0xe6, 0x78, 0x09, 0x66, 0x8a, 0xa3, 0xd7, 0xe6, 0x9f, 0xc3, 0x42, 0x71, 0x26, 0x88, 0xc8, 0x7b, 0x08, 0xdf, 0x57,
-    0x3c, 0x6b, 0x79, 0x42, 0x48, 0x0f, 0x1d, 0x25, 0xaf, 0xae, 0xe0, 0xc3, 0xcc, 0x3f, 0x97, 0x04, 0x76, 0x08, 0x7b, 0xf8,
-    0x2b, 0xfa, 0xaf, 0x0e, 0x39, 0x76, 0x7d, 0xee, 0x3d, 0x6a, 0xfa, 0x57, 0x63, 0x9d, 0x1b, 0x95, 0x12, 0xad, 0xfe, 0x9d,
-    0xc8, 0x75, 0xef, 0x81, 0xfa, 0x64, 0xd1, 0x4d, 0xd4, 0xbf, 0x0d, 0xd3, 0x79, 0xcf, 0x43, 0xff, 0x95, 0x21, 0x57, 0x7d,
-    0x09, 0x99, 0x62, 0xad, 0x19, 0x38, 0x87, 0xb7, 0xf9, 0xa6, 0x50, 0x7b, 0x08, 0xdd, 0xd7, 0x27, 0x3c, 0xc6, 0xd3, 0x4c,
-    0x61, 0xb4, 0xd0, 0xbe, 0x57, 0x20, 0x2f, 0x42, 0xd4, 0x59, 0x68, 0x7e, 0x8c, 0xe0, 0x23, 0xcb, 0xfe, 0x1d, 0x9a, 0x89,
-    0x5b, 0xf7, 0xe5, 0x4a, 0x43, 0xcd, 0x42, 0x1c, 0xdb, 0xd9, 0xfe, 0xeb, 0x60, 0xeb, 0x32, 0xbd, 0x88, 0xf4, 0xef, 0xc8,
-    0x22, 0x0f, 0xcf, 0x99, 0xb7, 0xfe, 0x2d, 0x99, 0xc4, 0xab, 0x6e, 0xdd, 0x35, 0x8a, 0x0e, 0xa2, 0xfe, 0x8f, 0x72, 0xbe,
-    0xeb, 0xd0, 0x93, 0xf4, 0x3f, 0xf4, 0xaa, 0x8b, 0x93, 0x23, 0xcc, 0x43, 0x11, 0x38, 0x87, 0x04, 0x96, 0x09, 0x5e, 0xe5,
-    0x82, 0xec, 0x21, 0x74, 0x5f, 0x33, 0xf9, 0x94, 0x11, 0x2c, 0x65, 0x9a, 0x10, 0xbb, 0x76, 0x76, 0x30, 0x82, 0xa6, 0x20,
-    0xfa, 0xf7, 0x17, 0x46, 0xda, 0x4c, 0x61, 0x21, 0x63, 0x0c, 0x3d, 0xc4, 0xad, 0x37, 0xb1, 0xcc, 0xd0, 0xd5, 0xe3, 0xd8,
-    0xfd, 0x49, 0x36, 0x94, 0x10, 0xb6, 0x9f, 0x61, 0x46, 0x9e, 0x77, 0x48, 0x2e, 0x22, 0xfd, 0xd3, 0xf8, 0x89, 0xbd, 0xdc,
-    0x5a, 0x48, 0xfd, 0xef, 0x65, 0x23, 0x71, 0x14, 0x33, 0x73, 0x0b, 0xc8, 0xfa, 0x3b, 0x8e, 0xf5, 0xd7, 0x48, 0xa1, 0xb8,
-    0xa8, 0xff, 0xa1, 0x39, 0x1e, 0xc3, 0x4e, 0x21, 0x76, 0x30, 0x85, 0x5f, 0x78, 0x83, 0x24, 0xd2, 0x58, 0xec, 0x7a, 0xb3,
-    0xbd, 0x34, 0x4b, 0xb7, 0xc6, 0xed, 0x1c, 0xda, 0xfe, 0x8f, 0xe0, 0x6b, 0xc1, 0x99, 0xb6, 0x33, 0xa4, 0x8f, 0x1c, 0x2b,
-    0xf8, 0xfb, 0x43, 0xf3, 0x63, 0x85, 0x30, 0x7b, 0x47, 0xe1, 0xeb, 0xff, 0xfc, 0x63, 0xd7, 0xe1, 0x0a, 0x3a, 0x45, 0xa8,
-    0x09, 0x0b, 0x5e, 0xff, 0x57, 0x0d, 0xc6, 0x77, 0xc8, 0xfa, 0x8f, 0x23, 0x81, 0x0e, 0xa4, 0x89, 0xfd, 0xcd, 0xc8, 0xfa,
-    0x17, 0x67, 0x3b, 0x37, 0x71, 0x87, 0x99, 0xa3, 0xca, 0xae, 0x7f, 0x09, 0x56, 0xd2, 0x93, 0x0e, 0x82, 0xfe, 0x31, 0xec,
-    0x08, 0xc9, 0xf1, 0x9a, 0x1c, 0x14, 0x1c, 0x3f, 0xce, 0x39, 0x3a, 0x73, 0xdd, 0xdc, 0x44, 0x19, 0x31, 0xdf, 0xfc, 0x7b,
-    0x88, 0x31, 0x75, 0xd8, 0x29, 0x20, 0x3a, 0xa3, 0xc3, 0xaf, 0xe7, 0x21, 0x31, 0xfe, 0x73, 0x7c, 0xc8, 0x93, 0x59, 0x7f,
-    0x21, 0x4e, 0x21, 0x7f, 0xfb, 0x73, 0x41, 0x2c, 0xab, 0x85, 0x6f, 0xff, 0x27, 0x84, 0xb4, 0x27, 0xaf, 0x32, 0xa1, 0x88,
-    0xda, 0xff, 0xab, 0xc8, 0x30, 0x8e, 0xd0, 0x38, 0xd2, 0xcc, 0xb8, 0xe5, 0xd2, 0x16, 0x31, 0xfc, 0xe8, 0x8e, 0x47, 0x1f,
-    0xad, 0xfe, 0xce, 0xdc, 0x05, 0x9f, 0xf0, 0x85, 0xb9, 0x13, 0xec, 0xfa, 0x3b, 0x51, 0x53, 0x59, 0xf4, 0x12, 0xdb, 0xff,
-    0x9f, 0x19, 0x16, 0xfc, 0xfd, 0x25, 0x96, 0x46, 0x9d, 0x6b, 0x81, 0x3d, 0x54, 0x60, 0x1d, 0x2d, 0xe9, 0xc9, 0xb2, 0x08,
-    0xf5, 0xf7, 0xd3, 0x9c, 0x4c, 0x35, 0x6e, 0x67, 0x89, 0x18, 0xf7, 0x5c, 0x9b, 0x34, 0x06, 0x50, 0x8f, 0xaa, 0x74, 0x61,
-    0x8f, 0x30, 0x82, 0xab, 0x7f, 0xfb, 0x2b, 0x19, 0x4e, 0xa6, 0xd8, 0x17, 0xf5, 0xa7, 0xfb, 0x89, 0x8d, 0xf2, 0x4a, 0x9c,
-    0x63, 0xbf, 0xc9, 0xb9, 0x9c, 0x4a, 0x27, 0xf6, 0x8a, 0xb1, 0x5a, 0x85, 0xd1, 0x3f, 0x96, 0x75, 0x74, 0x35, 0x71, 0x2d,
-    0x9b, 0x84, 0x37, 0x12, 0x81, 0x2d, 0xaa, 0xb0, 0x55, 0x8c, 0x0e, 0xcf, 0xbf, 0x9e, 0x0a, 0x16, 0xfd, 0xeb, 0xb1, 0x9d,
-    0x1d, 0x66, 0x66, 0x21, 0x2f, 0xfd, 0x7d, 0x7c, 0xc4, 0x76, 0x51, 0xff, 0x5a, 0x6c, 0xe3, 0x0d, 0xce, 0xa1, 0x26, 0xdd,
-    0xc9, 0x14, 0x47, 0x73, 0x8f, 0xa4, 0x7f, 0x60, 0x0f, 0x2d, 0xc8, 0x20, 0x2b, 0xc2, 0x9c, 0x64, 0x29, 0xc6, 0xf7, 0x7f,
-    0x90, 0x15, 0xbc, 0x67, 0x79, 0xc7, 0xd2, 0x90, 0x1f, 0xd9, 0x43, 0x2e, 0x8b, 0xc5, 0x28, 0x76, 0xff, 0xf6, 0xdb, 0xf8,
-    0x4e, 0x7c, 0x7a, 0x08, 0xa4, 0xfb, 0xff, 0x55, 0x8e, 0xfa, 0x4a, 0x2e, 0x61, 0x9c, 0x99, 0x35, 0x64, 0x3e, 0xb7, 0x44,
-    0xac, 0x3f, 0x0a, 0xde, 0xff, 0x6f, 0x6e, 0xa2, 0x02, 0x7e, 0x17, 0xe3, 0xcd, 0xf2, 0xb7, 0x68, 0x4e, 0xb6, 0x10, 0xb3,
-    0x9a, 0x7f, 0x3d, 0xcb, 0x2c, 0xfa, 0xfb, 0x98, 0xe6, 0xc6, 0x68, 0x7b, 0xeb, 0x5f, 0x9e, 0x2d, 0xa2, 0xfe, 0xce, 0x7c,
-    0x95, 0xe3, 0xc8, 0x64, 0x1f, 0xbf, 0xb9, 0xb1, 0x51, 0xd1, 0xe6, 0x5a, 0x60, 0x0f, 0x07, 0x59, 0xc7, 0x8a, 0x08, 0xe3,
-    0x4a, 0x14, 0x8c, 0x62, 0x62, 0x4b, 0x73, 0x74, 0x48, 0x38, 0x22, 0xc7, 0x2e, 0x73, 0x8c, 0xbf, 0x97, 0x2d, 0x16, 0x21,
-    0x66, 0xa2, 0x20, 0x7b, 0x48, 0x24, 0x86, 0x2f, 0xa2, 0x9a, 0xc9, 0x4b, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14,
-    0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x39, 0x31, 0xfc, 0xff, 0x43, 0x68, 0x67, 0xf5, 0xff, 0x3b,
-    0xf3, 0xae, 0xbf, 0xcb, 0xf7, 0xbc, 0x2a, 0xce, 0x3a, 0x1e, 0x3a, 0x42, 0xbf, 0x13, 0x07, 0xe0, 0xe5, 0x97, 0x6f, 0x25,
-    0x38, 0x8c, 0x5b, 0xf1, 0x6c, 0xc8, 0x5f, 0x6d, 0x85, 0x35, 0x5a, 0xd3, 0xde, 0xfd, 0xed, 0x1c, 0x92, 0x38, 0xdf, 0xfd,
-    0xfd, 0x71, 0x61, 0xbc, 0xdc, 0x33, 0x48, 0xe2, 0x4c, 0xf7, 0xf7, 0x93, 0xf9, 0x88, 0xba, 0xc2, 0x97, 0xdf, 0xb7, 0x83,
-    0xdf, 0xfc, 0x13, 0x18, 0x2c, 0xf8, 0x3f, 0xce, 0x3c, 0xe4, 0x7a, 0xc2, 0x67, 0x34, 0xa8, 0x41, 0x43, 0x1a, 0x0a, 0x4e,
-    0x86, 0x44, 0xeb, 0x77, 0xd6, 0xd2, 0x66, 0x8b, 0x8a, 0x62, 0xda, 0x85, 0x96, 0x31, 0x2f, 0x43, 0xff, 0x49, 0x23, 0x63,
-    0x97, 0xb3, 0x8e, 0x43, 0x5b, 0x8d, 0xa6, 0xe2, 0x48, 0xcb, 0xf6, 0xef, 0xff, 0x8e, 0x97, 0xbc, 0x17, 0x0b, 0x49, 0xb5,
-    0x8c, 0xbf, 0xde, 0x8e, 0xed, 0xbc, 0x45, 0x7b, 0x7e, 0x60, 0x8b, 0xe0, 0xd2, 0xac, 0x6f, 0xbc, 0x97, 0x23, 0x8d, 0x5b,
-    0xbe, 0x9b, 0xa0, 0x5e, 0xe8, 0xb7, 0xea, 0x01, 0x82, 0x5b, 0xf5, 0x6a, 0xb2, 0x83, 0x31, 0x0a, 0xb1, 0xa4, 0xf2, 0xb0,
-    0xa0, 0x7f, 0x60, 0xe4, 0xe0, 0xe7, 0xc9, 0xe5, 0xe5, 0xa0, 0x2f, 0x59, 0xfa, 0x06, 0x3e, 0x92, 0xa9, 0xae, 0x36, 0xdf,
-    0xf3, 0x9d, 0x90, 0x7e, 0x92, 0x99, 0xcb, 0xc0, 0xff, 0xfb, 0xb3, 0x6c, 0x14, 0xbc, 0x8d, 0xa7, 0x04, 0xfd, 0xa4, 0xaf,
-    0x91, 0xc7, 0xcd, 0x61, 0xe9, 0xfd, 0xd8, 0xcc, 0x2c, 0x61, 0x44, 0xb9, 0x01, 0xa2, 0xfb, 0xc5, 0x67, 0xfc, 0x20, 0xb3,
-    0xc8, 0x10, 0x47, 0x9f, 0x2e, 0xce, 0x06, 0xd2, 0x85, 0x52, 0xea, 0x78, 0x77, 0xf2, 0x91, 0x5c, 0x48, 0x5f, 0xf2, 0xba,
-    0x78, 0xac, 0x1e, 0x64, 0x33, 0x9f, 0x5d, 0x8c, 0x2f, 0xf0, 0x98, 0x77, 0xf9, 0xfa, 0xcc, 0xa1, 0xaf, 0x65, 0x8d, 0x7f,
-    0xb9, 0xea, 0x8c, 0xb6, 0x8c, 0x51, 0xed, 0xe3, 0x9f, 0x6c, 0x28, 0x80, 0xc3, 0x4b, 0xd2, 0xdf, 0x71, 0xe3, 0x04, 0x5c,
-    0x78, 0x8e, 0x27, 0x2a, 0xdc, 0xed, 0x56, 0x85, 0x3c, 0x77, 0x0c, 0xd9, 0xdf, 0xf8, 0x91, 0x19, 0xe6, 0xb7, 0xd3, 0x82,
-    0x23, 0xbf, 0x1e, 0x4a, 0x79, 0x36, 0x1a, 0x7d, 0x1f, 0x61, 0x0b, 0x27, 0x8b, 0xe7, 0x73, 0x0f, 0xbb, 0x4d, 0x4d, 0x77,
-    0x1a, 0x7b, 0x22, 0xcc, 0xff, 0xfe, 0x1d, 0x9f, 0x09, 0x4b, 0xfb, 0x09, 0xa3, 0x6f, 0xfa, 0xcc, 0xd8, 0xac, 0x73, 0x3d,
-    0xf6, 0x35, 0x49, 0xd4, 0xff, 0x31, 0x7e, 0xa4, 0x3b, 0x1f, 0x17, 0xa2, 0xd6, 0x3e, 0x99, 0xc9, 0x42, 0xbd, 0x50, 0x8a,
-    0xe1, 0xe6, 0xde, 0x2f, 0x43, 0x86, 0xc7, 0x8c, 0x13, 0x36, 0x7d, 0xde, 0x15, 0xfc, 0x33, 0x3e, 0x33, 0xb2, 0x6c, 0xcf,
-    0x88, 0x7b, 0x29, 0xbc, 0xfe, 0xce, 0xe8, 0xc3, 0x81, 0xf1, 0xcc, 0x3f, 0xb0, 0xc4, 0x2a, 0x2c, 0x32, 0x9e, 0x99, 0x92,
-    0x64, 0x73, 0x2d, 0x39, 0xc6, 0xb9, 0xf1, 0xa0, 0x65, 0xae, 0x0a, 0x1f, 0x37, 0xb0, 0x87, 0x46, 0xec, 0xb2, 0xf8, 0x85,
-    0xfc, 0x8e, 0x46, 0xe7, 0x3a, 0xbf, 0xe1, 0x7b, 0xcf, 0x2b, 0x7a, 0x90, 0x54, 0x71, 0x3c, 0x72, 0x9b, 0xfe, 0xa5, 0xc8,
-    0xb2, 0x8e, 0x5f, 0x2e, 0xeb, 0x1f, 0xc7, 0x6a, 0xae, 0xa1, 0x3c, 0xe9, 0x9c, 0x51, 0x84, 0xed, 0x79, 0x49, 0x2e, 0xe0,
-    0x6e, 0xb6, 0x78, 0x8c, 0x28, 0x2b, 0xeb, 0x53, 0x97, 0x54, 0x3a, 0x89, 0x6b, 0x5c, 0xc7, 0x56, 0x7e, 0xa3, 0x83, 0x38,
-    0x36, 0x6c, 0x51, 0xe8, 0x5f, 0x9b, 0x5c, 0x73, 0x47, 0x26, 0xb0, 0x5d, 0xa8, 0x6f, 0xfd, 0x79, 0xee, 0x94, 0x8b, 0xa6,
-    0xac, 0x35, 0xf5, 0xfe, 0xcd, 0x66, 0xd6, 0x90, 0x41, 0xd6, 0x73, 0x19, 0xcc, 0x1e, 0xcf, 0xf1, 0x4f, 0xcf, 0x34, 0x91,
-    0x19, 0x99, 0x9e, 0xb1, 0x05, 0xd5, 0xd9, 0x69, 0x99, 0x8f, 0xc0, 0xa6, 0xbf, 0x53, 0x3b, 0xb5, 0x8c, 0x4a, 0xff, 0xc6,
-    0x7c, 0x6a, 0x7e, 0x76, 0x8d, 0xe8, 0xe6, 0x2a, 0xb8, 0xe3, 0x67, 0x18, 0xbb, 0x98, 0xcb, 0x27, 0xac, 0xe1, 0xf1, 0x02,
-    0xeb, 0xbf, 0x91, 0x25, 0xac, 0x03, 0x86, 0x5b, 0x1d, 0x47, 0x65, 0xe8, 0x62, 0x1c, 0x73, 0x9f, 0x59, 0xfb, 0x38, 0x7f,
-    0x45, 0x7f, 0x1f, 0xb3, 0xcc, 0xcc, 0x13, 0xcd, 0xd9, 0x62, 0xe9, 0xd5, 0x34, 0x65, 0x33, 0x31, 0xbc, 0x6e, 0x9c, 0xc8,
-    0x43, 0xcc, 0x48, 0xce, 0x1b, 0x2c, 0x25, 0xc5, 0xdf, 0x06, 0xe6, 0x0a, 0xfd, 0xd0, 0x50, 0xba, 0x83, 0xc7, 0x5c, 0x17,
-    0x8e, 0xfb, 0xf7, 0x67, 0x86, 0x5b, 0xd2, 0xec, 0xfa, 0x77, 0xb7, 0xce, 0x1b, 0x64, 0xab, 0xff, 0x8b, 0x9a, 0xdb, 0x82,
-    0xad, 0xde, 0xc2, 0x28, 0xf4, 0x1f, 0xc8, 0xf5, 0xdc, 0xcf, 0x41, 0x61, 0xe6, 0xaa, 0xc3, 0xef, 0x9b, 0x64, 0x4b, 0xbc,
-    0xaa, 0x97, 0xfe, 0x4b, 0x43, 0x7c, 0xa1, 0x83, 0x2c, 0x2d, 0xcc, 0x93, 0xcc, 0x33, 0xa3, 0x20, 0xdb, 0x54, 0x2b, 0xc1,
-    0x5e, 0xce, 0x61, 0xae, 0x19, 0x07, 0xfc, 0x0e, 0x16, 0x70, 0x36, 0xfb, 0xac, 0x51, 0x8d, 0x17, 0x93, 0xc5, 0x9d, 0x64,
-    0x0b, 0x4e, 0xe5, 0xd0, 0xe7, 0x04, 0x8c, 0x3f, 0xd7, 0x46, 0x5b, 0x36, 0x5a, 0x67, 0xd1, 0xb1, 0xeb, 0x7f, 0x31, 0x6b,
-    0xfe, 0x66, 0xfd, 0xaf, 0x67, 0x07, 0x55, 0x88, 0xe1, 0x76, 0x72, 0xa3, 0xd0, 0xdf, 0x7f, 0x7f, 0x8e, 0x62, 0xb2, 0x98,
-    0x7e, 0x12, 0xbd, 0x82, 0xfd, 0xf3, 0x81, 0xd6, 0xd9, 0xa5, 0xec, 0xfa, 0x7f, 0x1a, 0xe2, 0x66, 0x9f, 0x21, 0x8e, 0x10,
-    0xee, 0xf4, 0x76, 0xf7, 0x51, 0x8b, 0x5d, 0x5c, 0xe4, 0x11, 0x09, 0xd0, 0x85, 0xfd, 0xe6, 0x19, 0xaa, 0x1c, 0xfb, 0xe8,
-    0xe4, 0x8e, 0x87, 0x2f, 0xb5, 0xc2, 0xcb, 0x4c, 0x19, 0xed, 0xc1, 0x3a, 0x8f, 0x91, 0x52, 0xbd, 0xf5, 0x3f, 0x8d, 0x5d,
-    0xc2, 0x73, 0x61, 0x64, 0xfd, 0x63, 0xd9, 0x6e, 0x9d, 0x79, 0xe3, 0xe8, 0xe8, 0x9f, 0xc0, 0x08, 0x0e, 0x90, 0xce, 0x8f,
-    0x4c, 0x8f, 0x5a, 0xff, 0xb3, 0xd8, 0x2f, 0xce, 0xdf, 0xe7, 0xe4, 0xfd, 0x50, 0xca, 0x11, 0xc7, 0x35, 0x6c, 0xe6, 0xd1,
-    0xa8, 0xf5, 0xbf, 0x91, 0x7d, 0x74, 0xa4, 0x32, 0x35, 0xe8, 0xcb, 0x6e, 0xf1, 0x59, 0xc7, 0xe7, 0xf6, 0xc6, 0x92, 0x3d,
-    0xce, 0xf2, 0x19, 0x96, 0x9b, 0x3a, 0xc2, 0xe1, 0x77, 0x56, 0x8a, 0xb3, 0xb9, 0xf9, 0xdb, 0xfe, 0x39, 0xa6, 0x0d, 0x89,
-    0x67, 0xb6, 0xb5, 0x06, 0xf7, 0xd6, 0x3f, 0x86, 0x5f, 0x18, 0x46, 0x25, 0xd7, 0xe3, 0x5f, 0x3a, 0x0a, 0xfd, 0x9d, 0x19,
-    0x78, 0x9e, 0xf8, 0x5b, 0xf5, 0xf7, 0xdf, 0xaf, 0x55, 0xa2, 0x5a, 0x3f, 0xbf, 0x7d, 0x1e, 0xcc, 0x52, 0x31, 0x52, 0xb4,
-    0x1e, 0xa3, 0x48, 0x67, 0x1f, 0x69, 0xf4, 0xb0, 0xce, 0x2b, 0x61, 0xd7, 0xdf, 0xc7, 0xed, 0x24, 0x73, 0x90, 0x03, 0xcc,
-    0xf3, 0x98, 0xfd, 0xea, 0x36, 0xb0, 0x6a, 0xea, 0x70, 0x3e, 0xf0, 0x5a, 0x30, 0x26, 0x06, 0x4b, 0x4d, 0x71, 0x13, 0x7b,
-    0xa8, 0x13, 0xec, 0x55, 0xee, 0xb1, 0xcc, 0x27, 0xe3, 0xad, 0x7f, 0x9d, 0x43, 0xde, 0xbd, 0xf4, 0x89, 0x4a, 0xff, 0xc7,
-    0xac, 0x73, 0xf8, 0x1d, 0x3d, 0xfd, 0x8f, 0x0c, 0xf1, 0x9e, 0xed, 0x65, 0x41, 0x4a, 0x65, 0xe9, 0x13, 0xe4, 0x7d, 0xa9,
-    0x97, 0xfe, 0x35, 0xd9, 0x61, 0x89, 0xb5, 0x3e, 0xde, 0xf5, 0x57, 0x42, 0x9f, 0x45, 0x2f, 0xb0, 0xd6, 0x83, 0xab, 0x84,
-    0x39, 0x52, 0x4a, 0x71, 0x01, 0x73, 0x55, 0xff, 0x13, 0x84, 0xa7, 0x98, 0xcf, 0x7c, 0xeb, 0xdb, 0xd5, 0x16, 0xc1, 0xef,
-    0x13, 0xf9, 0x9c, 0x63, 0xb6, 0xb8, 0x59, 0xf3, 0x4e, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14,
-    0x45, 0x51, 0x14, 0x45, 0x39, 0x8a, 0x0c, 0x15, 0x7d, 0xb4, 0x87, 0x7e, 0x11, 0x6f, 0x10, 0xfc, 0xaa, 0x6a, 0xc3, 0x19,
-    0xeb, 0xbe, 0x8c, 0x65, 0xeb, 0x0b, 0x69, 0x68, 0x75, 0xab, 0xfb, 0xdd, 0x3d, 0x31, 0x96, 0x6f, 0x8e, 0x97, 0x52, 0x8b,
-    0x58, 0x1a, 0x0b, 0x73, 0xb7, 0x7b, 0x9d, 0x75, 0x05, 0xfe, 0x10, 0x47, 0xa2, 0xaf, 0xc4, 0x1f, 0xc1, 0x91, 0x3f, 0x0b,
-    0x7a, 0xfd, 0x52, 0x6a, 0x23, 0x4a, 0x50, 0x8b, 0xcb, 0xcc, 0x59, 0x9f, 0xc1, 0x0d, 0xe2, 0x08, 0xfb, 0xce, 0x3a, 0x55,
-    0xb9, 0xc4, 0xfa, 0x9d, 0xc8, 0xc9, 0xd3, 0x73, 0x2c, 0x69, 0xce, 0xb6, 0xa7, 0xd3, 0xc4, 0x63, 0xdc, 0xb6, 0x04, 0x93,
-    0x33, 0x71, 0x34, 0x16, 0xf2, 0xd5, 0xd9, 0xba, 0x76, 0x84, 0x99, 0xfa, 0x43, 0xc9, 0xa1, 0x81, 0x67, 0x7a, 0x2d, 0x36,
-    0xb0, 0x96, 0xff, 0x92, 0x62, 0x9d, 0x6b, 0xc0, 0xef, 0x47, 0x96, 0x47, 0x19, 0xad, 0xc3, 0x46, 0x52, 0x48, 0x67, 0x89,
-    0x25, 0x97, 0xcb, 0x32, 0x9e, 0x83, 0xa2, 0xef, 0xfc, 0x22, 0xd2, 0x59, 0x4d, 0x06, 0xaf, 0x83, 0xe0, 0x68, 0xf0, 0x3a,
-    0xeb, 0x04, 0x7a, 0x8a, 0xb1, 0x2c, 0xce, 0xf2, 0x6a, 0x51, 0x5e, 0xbf, 0x94, 0x9a, 0x43, 0x3f, 0x96, 0xb1, 0x93, 0xe1,
-    0x74, 0x67, 0x2d, 0x1b, 0x59, 0x27, 0x1c, 0x2d, 0x87, 0x1e, 0x6c, 0x61, 0x2b, 0xab, 0xa9, 0x65, 0xcd, 0xd3, 0x54, 0x56,
-    0x88, 0xe7, 0x99, 0x43, 0x1f, 0xb2, 0x48, 0x61, 0x0f, 0xd7, 0x8b, 0xe7, 0xe4, 0xe4, 0xcc, 0x2a, 0x76, 0xd3, 0x5b, 0xcc,
-    0xf3, 0x1c, 0x86, 0x92, 0xe1, 0xe1, 0x8e, 0x95, 0xae, 0xd0, 0xab, 0xb4, 0x2d, 0xe7, 0x15, 0x62, 0x88, 0xe1, 0x5d, 0xfa,
-    0x79, 0x94, 0x45, 0x59, 0xff, 0x18, 0x96, 0x9b, 0xd8, 0x9d, 0x52, 0x7c, 0x23, 0x8c, 0x0c, 0xed, 0x70, 0xbf, 0x99, 0x75,
-    0xa2, 0xb8, 0xe0, 0xa5, 0xda, 0x60, 0xbc, 0x62, 0xe5, 0x19, 0x63, 0xd5, 0xbf, 0x9a, 0xf5, 0xfe, 0x6a, 0x64, 0x71, 0xb2,
-    0x4a, 0xcb, 0x03, 0x0a, 0xd7, 0xa1, 0x5e, 0x81, 0xf5, 0x7f, 0xd3, 0x9c, 0x59, 0x26, 0xa3, 0x89, 0x25, 0x96, 0xd5, 0x74,
-    0x10, 0xd6, 0x19, 0x49, 0x02, 0xc5, 0x98, 0x28, 0x7a, 0xe7, 0x03, 0x79, 0xfa, 0x0e, 0x33, 0xc5, 0x63, 0xce, 0x30, 0xe5,
-    0xf4, 0x61, 0x76, 0x08, 0xf1, 0x29, 0x81, 0x9c, 0xa9, 0xc8, 0x0f, 0x16, 0xfd, 0x47, 0x59, 0xc6, 0xf1, 0xb4, 0xe9, 0x3f,
-    0xc8, 0xa3, 0xb4, 0x9d, 0xc9, 0x01, 0x31, 0xfe, 0xe8, 0xf0, 0xb2, 0xf8, 0x86, 0x78, 0x2e, 0x8e, 0xd3, 0xba, 0x21, 0x17,
-    0x71, 0x11, 0xed, 0xd9, 0x21, 0x46, 0x3f, 0xbd, 0xc5, 0x4c, 0x1a, 0x0b, 0xe3, 0x82, 0xd6, 0x61, 0xbf, 0x5b, 0xeb, 0xdf,
-    0x6c, 0xd1, 0xbf, 0xaf, 0xc7, 0xfd, 0x65, 0xbb, 0xa7, 0x73, 0x45, 0x35, 0x9d, 0x65, 0xb7, 0x92, 0x26, 0xfa, 0x5f, 0x65,
-    0xfd, 0x1b, 0xbb, 0xf1, 0x32, 0x7e, 0xe7, 0xd4, 0x30, 0xc1, 0xb7, 0x9a, 0xe3, 0xc6, 0x75, 0xb5, 0x67, 0xba, 0x47, 0x9e,
-    0x56, 0x22, 0xcf, 0x9d, 0xdf, 0xe2, 0xd0, 0x6d, 0x03, 0x2e, 0xb2, 0xcd, 0xc2, 0x5c, 0x33, 0xb5, 0xc9, 0x71, 0xbf, 0x3e,
-    0xdf, 0x6e, 0xd1, 0xff, 0xf2, 0xa8, 0xda, 0x7f, 0xef, 0xd2, 0x76, 0x03, 0xa9, 0x1e, 0xdb, 0x46, 0x2a, 0x8b, 0x37, 0xb0,
-    0x9f, 0xd9, 0x41, 0xc2, 0x1d, 0x40, 0xa3, 0x59, 0xc7, 0x4e, 0x66, 0x0a, 0xbe, 0xc3, 0xa6, 0xa4, 0xb9, 0xf7, 0xf6, 0x59,
-    0x16, 0xfd, 0x47, 0x79, 0xdc, 0x5f, 0xd1, 0xea, 0xdf, 0x8c, 0x34, 0x2e, 0x8d, 0xa2, 0xfe, 0x6f, 0xe0, 0xea, 0xff, 0xa0,
-    0xf9, 0xf9, 0xa1, 0x50, 0xdb, 0x06, 0xd6, 0x79, 0x40, 0x88, 0x0a, 0x0a, 0xcd, 0xd3, 0x2c, 0xc1, 0x13, 0x97, 0x13, 0x1c,
-    0xff, 0x7b, 0xbe, 0x10, 0x6f, 0x75, 0x63, 0x70, 0xeb, 0xb3, 0x2d, 0xfa, 0x37, 0x88, 0x52, 0x7f, 0xaf, 0xd2, 0x56, 0x8d,
-    0x83, 0x6e, 0xff, 0x26, 0x51, 0xd0, 0x2f, 0x52, 0x59, 0xac, 0xce, 0x41, 0xcf, 0xa8, 0x11, 0xc7, 0xd7, 0xf9, 0x93, 0xb8,
-    0xbc, 0x0a, 0xb8, 0x51, 0x8c, 0x6d, 0x2d, 0xfa, 0x07, 0xee, 0xaf, 0x19, 0x7f, 0x59, 0xff, 0x2e, 0xec, 0xb4, 0xce, 0xe9,
-    0x74, 0x24, 0xf4, 0xcf, 0xcf, 0xd3, 0x1a, 0xe4, 0x0a, 0xb9, 0x9a, 0xe3, 0xce, 0x3b, 0x14, 0xcf, 0x2e, 0xa1, 0x1f, 0x77,
-    0x5a, 0x70, 0x44, 0xf8, 0x36, 0x45, 0xa4, 0xbf, 0x57, 0x69, 0xf3, 0x31, 0x9d, 0x8f, 0x29, 0x4b, 0x79, 0xbe, 0x13, 0xa2,
-    0x1b, 0x22, 0x95, 0x45, 0xc7, 0xab, 0xfb, 0xa9, 0x69, 0xdf, 0xdf, 0x76, 0x67, 0x3c, 0x29, 0xa8, 0xfe, 0x3e, 0xc6, 0xb2,
-    0x88, 0x56, 0x3c, 0xc1, 0x42, 0xcf, 0xfe, 0xdf, 0x03, 0xe2, 0x08, 0xca, 0xd1, 0xe9, 0xbf, 0xd9, 0x44, 0x0c, 0x1c, 0x3d,
-    0xfd, 0x03, 0x79, 0x5a, 0x81, 0x6f, 0xc5, 0x18, 0xb4, 0x1c, 0xf3, 0xfc, 0x52, 0x82, 0xbe, 0xfc, 0x29, 0xb6, 0xbd, 0x0b,
-    0xcc, 0xb3, 0xd6, 0x05, 0xcc, 0x2a, 0x22, 0xfd, 0xbd, 0x4a, 0x9b, 0xd3, 0x46, 0x4d, 0x24, 0x93, 0x0c, 0xc6, 0x08, 0xcf,
-    0x77, 0x91, 0xca, 0xa2, 0xb3, 0xf5, 0x2f, 0x64, 0x91, 0xc1, 0x74, 0x4b, 0xb4, 0x95, 0x5d, 0xff, 0x52, 0x0c, 0x64, 0x21,
-    0x13, 0xb8, 0xea, 0x88, 0xeb, 0x7f, 0x25, 0x0f, 0x90, 0x61, 0x71, 0xa6, 0x1f, 0x19, 0xfd, 0xfd, 0x79, 0x9a, 0xc9, 0x58,
-    0x71, 0x6e, 0x95, 0x1c, 0x9e, 0x66, 0x07, 0x7b, 0x59, 0xca, 0xb9, 0x96, 0xf8, 0x84, 0xb9, 0xec, 0x65, 0x0f, 0xed, 0x8b,
-    0x48, 0xff, 0xa5, 0x9e, 0xa5, 0xcd, 0x5f, 0xf7, 0xdb, 0x7a, 0x94, 0x81, 0xb2, 0x38, 0xd3, 0x63, 0x96, 0x89, 0x52, 0x1e,
-    0x33, 0xc8, 0xd8, 0xf5, 0x2f, 0xeb, 0xc6, 0x71, 0x37, 0x21, 0x5b, 0x38, 0xaf, 0xa2, 0xd4, 0xbf, 0x81, 0x89, 0x2a, 0x5b,
-    0x66, 0xd1, 0xa2, 0xc1, 0x11, 0x7a, 0xef, 0x92, 0x68, 0x8d, 0xb6, 0x73, 0x8e, 0x19, 0x6f, 0x89, 0x5e, 0x0e, 0x50, 0x91,
-    0x62, 0xd6, 0x67, 0x6e, 0x5f, 0x94, 0xfa, 0xb7, 0xf3, 0x2c, 0x6d, 0xde, 0x04, 0xca, 0xe2, 0x43, 0x85, 0x3c, 0x17, 0xbb,
-    0xfe, 0x3f, 0x33, 0x82, 0x2b, 0xb8, 0x93, 0xd9, 0x8c, 0xf1, 0xd0, 0xa5, 0xa8, 0xf4, 0x2f, 0xc6, 0x2c, 0xbe, 0x11, 0x9e,
-    0x26, 0x8f, 0x9c, 0xfe, 0x85, 0x7f, 0x23, 0x13, 0xe9, 0x9d, 0x4b, 0x61, 0xdc, 0xfd, 0x27, 0xff, 0x85, 0xad, 0x2b, 0x16,
-    0x78, 0xac, 0x81, 0x68, 0xf4, 0xaf, 0x44, 0x6f, 0xa6, 0x30, 0x95, 0x5e, 0x9e, 0xcf, 0x9f, 0xd1, 0x50, 0x9c, 0x83, 0xe2,
-    0xd3, 0xe2, 0xb1, 0xc6, 0x18, 0x61, 0x94, 0x0d, 0x9f, 0x38, 0x06, 0xc8, 0x58, 0x8f, 0x18, 0x37, 0xe5, 0xf0, 0x08, 0x94,
-    0x0f, 0x58, 0xa5, 0xf9, 0xf0, 0x3f, 0xcb, 0x69, 0xf4, 0x13, 0xdf, 0xf0, 0x29, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a,
-    0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0xca, 0x89, 0x4a, 0x15, 0xae, 0x16, 0x7c, 0x88, 0x7e, 0x26, 0x72, 0x9f,
-    0xa7, 0x97, 0xb6, 0x32, 0xe7, 0x09, 0x0e, 0xc7, 0x5a, 0xc1, 0xaf, 0xdc, 0x8d, 0x85, 0xb1, 0xf2, 0x6b, 0xb8, 0x6e, 0x8b,
-    0xc4, 0xa0, 0x83, 0xfd, 0x22, 0x61, 0x7c, 0xfc, 0x4a, 0x34, 0x72, 0xbf, 0xc8, 0xd6, 0x0d, 0x8e, 0xea, 0x2f, 0x1d, 0xa3,
-    0x8c, 0x78, 0x0c, 0xc7, 0x1d, 0x1f, 0xa0, 0x92, 0x47, 0xac, 0xc0, 0xe1, 0x4b, 0x2e, 0xe5, 0x95, 0xb0, 0x65, 0x25, 0x3d,
-    0x1c, 0xb5, 0x31, 0xc7, 0xbd, 0xfa, 0x9d, 0xc9, 0x60, 0x0a, 0x69, 0xc2, 0x75, 0x3b, 0x0e, 0xdd, 0x3f, 0x78, 0x55, 0x9c,
-    0x4f, 0xc0, 0xeb, 0x0b, 0xfc, 0x43, 0xac, 0x75, 0xf3, 0xa5, 0x0b, 0x0b, 0x85, 0x6d, 0x5b, 0xba, 0xa3, 0x05, 0x3e, 0x0a,
-    0xc6, 0x7d, 0x56, 0x91, 0x5c, 0x21, 0xc2, 0xa4, 0x1c, 0xeb, 0xcd, 0xc8, 0xf0, 0x75, 0xc8, 0x14, 0x9c, 0x49, 0xf9, 0xc7,
-    0xe8, 0x2c, 0x1e, 0x23, 0x91, 0x55, 0x86, 0xf5, 0xc0, 0x5d, 0x56, 0xef, 0xc2, 0xd4, 0xb0, 0xa5, 0xb7, 0x31, 0x2e, 0x6c,
-    0xd9, 0x39, 0x6c, 0x63, 0xb0, 0xb8, 0x8f, 0x16, 0xa4, 0xb1, 0xc9, 0xdc, 0x21, 0x6d, 0xe9, 0x7a, 0x5c, 0xaa, 0x9f, 0x40,
-    0x26, 0xd7, 0x9a, 0x3a, 0x60, 0xb0, 0xe0, 0xc3, 0x1f, 0x4d, 0x26, 0xab, 0xc4, 0x51, 0x7b, 0xbd, 0xf4, 0x2f, 0xc3, 0x6e,
-    0xd7, 0x55, 0xb8, 0x80, 0x8e, 0xa2, 0x36, 0xfb, 0x4c, 0xf4, 0xcb, 0x57, 0x2c, 0x30, 0xa5, 0xae, 0x15, 0xcb, 0xc5, 0x73,
-    0xbb, 0x9e, 0xed, 0x54, 0x66, 0xb2, 0x38, 0xb3, 0x42, 0x19, 0x32, 0x3d, 0x8f, 0x11, 0x60, 0x30, 0xbf, 0x59, 0xc6, 0xe4,
-    0xeb, 0xc0, 0x48, 0xe1, 0xee, 0x95, 0xf4, 0x77, 0xdc, 0x48, 0x19, 0xe2, 0xe8, 0xee, 0xb3, 0x79, 0x82, 0xcb, 0x59, 0xc3,
-    0x64, 0x76, 0x58, 0x47, 0x9b, 0x3c, 0xd6, 0xf9, 0x0f, 0x5f, 0x70, 0xb1, 0x25, 0x8f, 0x9c, 0x2b, 0x7c, 0xc8, 0xd3, 0xa5,
-    0x22, 0x3b, 0x70, 0x86, 0xf1, 0xbe, 0xb9, 0x6f, 0xf6, 0x59, 0xe6, 0x3d, 0x99, 0x40, 0x67, 0xe2, 0x48, 0xe3, 0x0e, 0x66,
-    0xe1, 0x23, 0x89, 0xfe, 0x96, 0xa3, 0xbf, 0xcb, 0x42, 0x96, 0x58, 0xfc, 0x25, 0x49, 0xe6, 0x18, 0xf5, 0xd9, 0xe7, 0x31,
-    0xee, 0x7e, 0x6b, 0x52, 0xad, 0xe3, 0xa1, 0x8e, 0x14, 0xe2, 0x36, 0x6c, 0xfa, 0x3b, 0x6e, 0x3f, 0x69, 0x4c, 0xe0, 0xda,
-    0xe6, 0x9e, 0x49, 0xa4, 0x95, 0xc5, 0x3f, 0x7e, 0x3c, 0x50, 0x83, 0x21, 0x6c, 0x25, 0x35, 0x38, 0xcb, 0x4e, 0x51, 0xe8,
-    0x7f, 0x39, 0xe9, 0x14, 0xe3, 0x35, 0xeb, 0x78, 0xd1, 0xff, 0xe2, 0x27, 0x2e, 0x61, 0x1e, 0xc5, 0xd8, 0x49, 0x59, 0x36,
-    0x5a, 0xfd, 0xd7, 0x57, 0x82, 0xb5, 0x5e, 0x6d, 0x62, 0x8e, 0xd1, 0xc7, 0x7a, 0x0c, 0x1f, 0xe7, 0xb1, 0x2b, 0xe8, 0x6e,
-    0x0e, 0x47, 0x1e, 0xad, 0xd7, 0xa6, 0xff, 0xef, 0x96, 0xf9, 0x00, 0x4e, 0x0c, 0x62, 0xf8, 0x37, 0x79, 0xa2, 0x3b, 0xca,
-    0xa6, 0xff, 0x56, 0x9a, 0x9a, 0x9f, 0xaf, 0x8a, 0xfa, 0x3b, 0x91, 0x5f, 0x2d, 0x58, 0x43, 0x33, 0xab, 0x1b, 0x63, 0x37,
-    0xcf, 0xf3, 0xaa, 0x99, 0x65, 0xa5, 0x13, 0xdb, 0x2d, 0xb5, 0x4f, 0x09, 0x92, 0x79, 0x99, 0x34, 0x4b, 0x0c, 0x41, 0x0c,
-    0x2b, 0x69, 0xc1, 0x9f, 0xd6, 0xb1, 0xba, 0xcb, 0xb1, 0xf2, 0x90, 0xb9, 0xa5, 0x0e, 0x6f, 0xf7, 0x96, 0x8b, 0xae, 0x6f,
-    0x9b, 0xfe, 0x63, 0x82, 0x33, 0x0f, 0x9d, 0x58, 0x54, 0x66, 0xbd, 0x89, 0x41, 0xb9, 0x8c, 0x03, 0xd4, 0x8e, 0x42, 0xff,
-    0x0f, 0x19, 0xcb, 0x29, 0x5c, 0x4a, 0xb2, 0xa8, 0xbf, 0x8f, 0x7f, 0x33, 0x9d, 0x8d, 0xd6, 0x56, 0xc5, 0x99, 0xd3, 0xe5,
-    0x4f, 0x2e, 0x33, 0xf3, 0x78, 0xad, 0x75, 0xe7, 0xc1, 0x08, 0x67, 0x20, 0x93, 0x89, 0xa1, 0x0f, 0x93, 0x85, 0xfe, 0xbd,
-    0xcf, 0xcc, 0x07, 0x35, 0x83, 0x0d, 0x96, 0x63, 0xc4, 0xf0, 0x1d, 0x93, 0xa8, 0x4c, 0x25, 0x2a, 0x09, 0x4e, 0xdb, 0xb2,
-    0x6c, 0x65, 0x80, 0xd8, 0xab, 0xb7, 0xe9, 0x5f, 0x97, 0xb9, 0x1e, 0x23, 0x8a, 0x1f, 0xcf, 0xb4, 0x27, 0x95, 0x1d, 0xec,
-    0xb6, 0xcc, 0x88, 0xf1, 0xb5, 0xb9, 0x4b, 0xc3, 0xa9, 0xcf, 0x78, 0x72, 0x98, 0xcf, 0x93, 0x16, 0xfd, 0x4f, 0xe1, 0x00,
-    0xbd, 0x3c, 0x8e, 0xfa, 0x0a, 0xe9, 0x46, 0x39, 0x67, 0x26, 0xa7, 0xbb, 0xc5, 0x35, 0xae, 0x62, 0xb7, 0x19, 0x4f, 0xbf,
-    0x04, 0xcb, 0x2d, 0xe7, 0x56, 0x9d, 0x5c, 0xeb, 0x31, 0xce, 0x0f, 0x19, 0xc5, 0xfb, 0x05, 0xc1, 0x0b, 0xfa, 0x3a, 0x2b,
-    0x44, 0x9f, 0xa5, 0x4d, 0xff, 0x76, 0x2c, 0xa5, 0xd5, 0x09, 0x5b, 0xff, 0x9f, 0x62, 0xb9, 0xc3, 0x9c, 0x56, 0x76, 0x25,
-    0xdb, 0x2c, 0xa9, 0xc5, 0xff, 0xe6, 0xb3, 0xae, 0xca, 0x7e, 0xeb, 0x8c, 0x0b, 0x91, 0x99, 0x14, 0x9c, 0x77, 0xac, 0x20,
-    0xfa, 0x4f, 0x16, 0xd7, 0xfe, 0xdf, 0x20, 0xf1, 0x98, 0x3c, 0xab, 0x3b, 0xf9, 0xa5, 0x50, 0x73, 0xa7, 0x05, 0xf8, 0x32,
-    0xca, 0xfe, 0x5f, 0x73, 0x7d, 0x53, 0x78, 0x0c, 0x11, 0xcf, 0xd8, 0x90, 0x99, 0x69, 0x0a, 0xc3, 0x10, 0x9e, 0x8f, 0x42,
-    0xff, 0x65, 0x34, 0xd2, 0x5c, 0x3f, 0xa1, 0xb8, 0x95, 0xa5, 0xc2, 0x2c, 0x51, 0x92, 0xfe, 0xa5, 0x78, 0x82, 0x34, 0x8f,
-    0xbe, 0xac, 0x72, 0x3c, 0x52, 0x9c, 0xb6, 0x42, 0xe4, 0xa6, 0xa4, 0x7f, 0x5d, 0xde, 0x15, 0x47, 0x14, 0x52, 0x4e, 0x3c,
-    0x6c, 0xf5, 0xbf, 0xa2, 0xfa, 0x2b, 0xaa, 0xbf, 0x72, 0x62, 0x53, 0xc2, 0xe3, 0x6b, 0x92, 0xa2, 0x28, 0x8a, 0xa2, 0x28,
-    0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2,
-    0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a,
-    0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28,
-    0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2,
-    0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a,
-    0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28,
-    0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2,
-    0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0x8a,
-    0xa2, 0x28, 0x8a, 0xe2, 0xe0, 0xff, 0xa7, 0xf9, 0xa0, 0xfa, 0x2b, 0xff, 0xb3, 0xfa, 0xff, 0x3f };
-
-// Font glyphs rectangles data (on atlas)
-static const Rectangle amberFontRecs[95] = {
-    { 4, 4, 3 , 16 },
-    { 15, 4, 2 , 11 },
-    { 25, 4, 4 , 5 },
-    { 37, 4, 9 , 10 },
-    { 54, 4, 8 , 14 },
-    { 70, 4, 11 , 11 },
-    { 89, 4, 9 , 11 },
-    { 106, 4, 2 , 5 },
-    { 116, 4, 4 , 13 },
-    { 128, 4, 4 , 13 },
-    { 140, 4, 6 , 6 },
-    { 154, 4, 7 , 7 },
-    { 169, 4, 3 , 5 },
-    { 180, 4, 6 , 2 },
-    { 194, 4, 2 , 3 },
-    { 204, 4, 5 , 13 },
-    { 217, 4, 8 , 11 },
-    { 233, 4, 5 , 10 },
-    { 4, 28, 8 , 10 },
-    { 20, 28, 8 , 11 },
-    { 36, 28, 8 , 10 },
-    { 52, 28, 7 , 11 },
-    { 67, 28, 8 , 11 },
-    { 83, 28, 7 , 10 },
-    { 98, 28, 8 , 11 },
-    { 114, 28, 8 , 11 },
-    { 130, 28, 2 , 8 },
-    { 140, 28, 3 , 10 },
-    { 151, 28, 7 , 8 },
-    { 166, 28, 7 , 5 },
-    { 181, 28, 7 , 8 },
-    { 196, 28, 7 , 11 },
-    { 211, 28, 12 , 13 },
-    { 231, 28, 9 , 10 },
-    { 4, 52, 7 , 10 },
-    { 19, 52, 9 , 11 },
-    { 36, 52, 8 , 10 },
-    { 52, 52, 7 , 10 },
-    { 67, 52, 6 , 10 },
-    { 81, 52, 10 , 11 },
-    { 99, 52, 8 , 10 },
-    { 115, 52, 2 , 10 },
-    { 125, 52, 7 , 11 },
-    { 140, 52, 8 , 10 },
-    { 156, 52, 6 , 10 },
-    { 170, 52, 10 , 10 },
-    { 188, 52, 8 , 10 },
-    { 204, 52, 10 , 11 },
-    { 222, 52, 7 , 10 },
-    { 237, 52, 10 , 11 },
-    { 4, 76, 8 , 10 },
-    { 20, 76, 8 , 11 },
-    { 36, 76, 8 , 10 },
-    { 52, 76, 8 , 11 },
-    { 68, 76, 9 , 10 },
-    { 85, 76, 13 , 10 },
-    { 106, 76, 9 , 10 },
-    { 123, 76, 9 , 10 },
-    { 140, 76, 8 , 10 },
-    { 156, 76, 4 , 13 },
-    { 168, 76, 5 , 13 },
-    { 181, 76, 4 , 13 },
-    { 193, 76, 6 , 5 },
-    { 207, 76, 7 , 2 },
-    { 222, 76, 4 , 3 },
-    { 234, 76, 7 , 9 },
-    { 4, 100, 7 , 11 },
-    { 19, 100, 7 , 9 },
-    { 34, 100, 8 , 11 },
-    { 50, 100, 8 , 9 },
-    { 66, 100, 5 , 11 },
-    { 79, 100, 8 , 11 },
-    { 95, 100, 6 , 10 },
-    { 109, 100, 3 , 10 },
-    { 120, 100, 4 , 13 },
-    { 132, 100, 7 , 10 },
-    { 147, 100, 2 , 10 },
-    { 157, 100, 10 , 8 },
-    { 175, 100, 6 , 8 },
-    { 189, 100, 8 , 9 },
-    { 205, 100, 7 , 11 },
-    { 220, 100, 8 , 11 },
-    { 236, 100, 4 , 8 },
-    { 4, 124, 7 , 9 },
-    { 19, 124, 5 , 10 },
-    { 32, 124, 6 , 9 },
-    { 46, 124, 8 , 8 },
-    { 62, 124, 11 , 8 },
-    { 81, 124, 7 , 8 },
-    { 96, 124, 8 , 11 },
-    { 112, 124, 7 , 8 },
-    { 127, 124, 5 , 13 },
-    { 140, 124, 2 , 17 },
-    { 150, 124, 5 , 13 },
-    { 163, 124, 7 , 4 },
-};
-
-// Font glyphs info data
-// NOTE: No glyphs.image data provided
-static const GlyphInfo amberFontGlyphs[95] = {
-    { 32, 0, 12, 3, { 0 }},
-    { 33, 1, 2, 3, { 0 }},
-    { 34, 1, 2, 6, { 0 }},
-    { 35, 0, 2, 8, { 0 }},
-    { 36, 0, 0, 8, { 0 }},
-    { 37, 1, 2, 12, { 0 }},
-    { 38, 0, 2, 8, { 0 }},
-    { 39, 1, 2, 3, { 0 }},
-    { 40, 1, 1, 4, { 0 }},
-    { 41, 0, 1, 4, { 0 }},
-    { 42, 0, 2, 6, { 0 }},
-    { 43, 1, 5, 8, { 0 }},
-    { 44, 0, 10, 3, { 0 }},
-    { 45, 0, 7, 6, { 0 }},
-    { 46, 1, 10, 3, { 0 }},
-    { 47, 0, 1, 4, { 0 }},
-    { 48, 0, 2, 8, { 0 }},
-    { 49, 0, 2, 5, { 0 }},
-    { 50, 0, 2, 8, { 0 }},
-    { 51, 0, 2, 8, { 0 }},
-    { 52, 0, 2, 8, { 0 }},
-    { 53, 1, 2, 8, { 0 }},
-    { 54, 0, 2, 8, { 0 }},
-    { 55, 0, 2, 7, { 0 }},
-    { 56, 0, 2, 8, { 0 }},
-    { 57, 0, 2, 8, { 0 }},
-    { 58, 1, 5, 3, { 0 }},
-    { 59, 0, 5, 3, { 0 }},
-    { 60, 1, 4, 8, { 0 }},
-    { 61, 1, 6, 8, { 0 }},
-    { 62, 1, 4, 8, { 0 }},
-    { 63, 0, 2, 6, { 0 }},
-    { 64, 0, 2, 12, { 0 }},
-    { 65, 0, 2, 9, { 0 }},
-    { 66, 1, 2, 8, { 0 }},
-    { 67, 0, 2, 9, { 0 }},
-    { 68, 1, 2, 9, { 0 }},
-    { 69, 1, 2, 7, { 0 }},
-    { 70, 1, 2, 7, { 0 }},
-    { 71, 0, 2, 9, { 0 }},
-    { 72, 1, 2, 9, { 0 }},
-    { 73, 1, 2, 3, { 0 }},
-    { 74, 0, 2, 7, { 0 }},
-    { 75, 1, 2, 8, { 0 }},
-    { 76, 1, 2, 7, { 0 }},
-    { 77, 1, 2, 11, { 0 }},
-    { 78, 1, 2, 9, { 0 }},
-    { 79, 0, 2, 10, { 0 }},
-    { 80, 1, 2, 8, { 0 }},
-    { 81, 0, 2, 10, { 0 }},
-    { 82, 1, 2, 8, { 0 }},
-    { 83, 0, 2, 8, { 0 }},
-    { 84, 0, 2, 8, { 0 }},
-    { 85, 1, 2, 9, { 0 }},
-    { 86, 0, 2, 9, { 0 }},
-    { 87, 0, 2, 13, { 0 }},
-    { 88, 0, 2, 9, { 0 }},
-    { 89, 0, 2, 8, { 0 }},
-    { 90, 0, 2, 8, { 0 }},
-    { 91, 1, 1, 4, { 0 }},
-    { 92, 0, 1, 4, { 0 }},
-    { 93, 0, 1, 4, { 0 }},
-    { 94, 0, 2, 6, { 0 }},
-    { 95, 0, 12, 6, { 0 }},
-    { 96, 0, 1, 4, { 0 }},
-    { 97, 0, 4, 7, { 0 }},
-    { 98, 1, 2, 8, { 0 }},
-    { 99, 0, 4, 7, { 0 }},
-    { 100, 0, 2, 8, { 0 }},
-    { 101, 0, 4, 7, { 0 }},
-    { 102, 0, 1, 4, { 0 }},
-    { 103, 0, 4, 8, { 0 }},
-    { 104, 1, 2, 7, { 0 }},
-    { 105, 0, 2, 3, { 0 }},
-    { 106, -1, 2, 3, { 0 }},
-    { 107, 1, 2, 7, { 0 }},
-    { 108, 1, 2, 3, { 0 }},
-    { 109, 1, 4, 11, { 0 }},
-    { 110, 1, 4, 7, { 0 }},
-    { 111, 0, 4, 7, { 0 }},
-    { 112, 1, 4, 8, { 0 }},
-    { 113, 0, 4, 8, { 0 }},
-    { 114, 1, 4, 4, { 0 }},
-    { 115, 0, 4, 6, { 0 }},
-    { 116, 0, 3, 4, { 0 }},
-    { 117, 1, 4, 7, { 0 }},
-    { 118, 0, 4, 7, { 0 }},
-    { 119, 0, 4, 10, { 0 }},
-    { 120, 0, 4, 7, { 0 }},
-    { 121, 0, 4, 7, { 0 }},
-    { 122, 0, 4, 7, { 0 }},
-    { 123, 0, 1, 5, { 0 }},
-    { 124, 1, -1, 4, { 0 }},
-    { 125, 0, 1, 5, { 0 }},
-    { 126, 1, 6, 8, { 0 }},
-};
-
-// Style loading function: Amber
-static void GuiLoadStyleAmber(void)
-{
-    // Load style properties provided
-    // NOTE: Default properties are propagated
-    for (int i = 0; i < AMBER_STYLE_PROPS_COUNT; i++)
-    {
-        GuiSetStyle(amberStyleProps[i].controlId, amberStyleProps[i].propertyId, amberStyleProps[i].propertyValue);
-    }
-
-    // Custom font loading
-    // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function
-    int amberFontDataSize = 0;
-    unsigned char *data = DecompressData(amberFontData, AMBER_STYLE_FONT_ATLAS_COMP_SIZE, &amberFontDataSize);
-    Image imFont = { data, 256, 256, 1, 2 };
-
-    Font font = { 0 };
-    font.baseSize = 16;
-    font.glyphCount = 95;
-
-    // Load texture from image
-    font.texture = LoadTextureFromImage(imFont);
-    UnloadImage(imFont);  // Uncompressed image data can be unloaded from memory
-
-    // Copy char recs data from global fontRecs
-    // NOTE: Required to avoid issues if trying to free font
-    font.recs = (Rectangle *)RAYGUI_MALLOC(font.glyphCount*sizeof(Rectangle));
-    memcpy(font.recs, amberFontRecs, font.glyphCount*sizeof(Rectangle));
-
-    // Copy font char info data from global fontChars
-    // NOTE: Required to avoid issues if trying to free font
-    font.glyphs = (GlyphInfo *)RAYGUI_MALLOC(font.glyphCount*sizeof(GlyphInfo));
-    memcpy(font.glyphs, amberFontGlyphs, font.glyphCount*sizeof(GlyphInfo));
-
-    GuiSetFont(font);
-
-    // Setup a white rectangle on the font to be used on shapes drawing,
-    // it makes possible to draw shapes and text (full UI) in a single draw call
-    Rectangle fontWhiteRec = { 254, 126, 1, 1 };
+// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+//                                                                              //
+//////////////////////////////////////////////////////////////////////////////////
+
+#define AMBER_STYLE_PROPS_COUNT  16
+
+// Custom style name: Amber
+static const GuiStyleProp amberStyleProps[AMBER_STYLE_PROPS_COUNT] = {
+    { 0, 0, (int)0x898988ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x292929ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0xd4d4d4ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xeb891dff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0x292929ff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0xffffffff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0xf1cf9dff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0xf39333ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0x282020ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x6a6a6aff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x818181ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x606060ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x00000010 },    // DEFAULT_TEXT_SIZE 
+    { 0, 18, (int)0xef922aff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x333333ff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+};
+
+// WARNING: This style uses a custom font: "hello-world.ttf" (size: 16, spacing: 1)
+
+#define AMBER_STYLE_FONT_ATLAS_COMP_SIZE 2605
+
+// Font atlas image pixels data: DEFLATE compressed
+static unsigned char amberFontData[AMBER_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
+    0xdd, 0x8b, 0x8e, 0x9c, 0xb8, 0x12, 0x00, 0x50, 0xf8, 0xff, 0x7f, 0x76, 0x5d, 0xe9, 0x66, 0xb3, 0xd2, 0xee, 0x8e, 0x6d,
+    0xaa, 0x30, 0x8f, 0xee, 0x9c, 0x1c, 0x45, 0x91, 0x9a, 0x34, 0x18, 0xdb, 0x85, 0x0d, 0x33, 0x94, 0x63, 0x03, 0x00, 0x00,
+    0x00, 0x88, 0x2d, 0x5a, 0xe7, 0xb3, 0xcc, 0xe7, 0x5b, 0xf7, 0xf3, 0xf6, 0xd7, 0xe7, 0x6d, 0xb0, 0xed, 0xe8, 0xbe, 0xfa,
+    0x65, 0xda, 0x92, 0xfb, 0x89, 0xee, 0x37, 0xe2, 0x87, 0x4f, 0x7e, 0xff, 0xc9, 0xec, 0xa7, 0x77, 0xbe, 0xf9, 0xda, 0xcb,
+    0x6e, 0x19, 0x1f, 0xbd, 0x25, 0x4b, 0x56, 0xff, 0xce, 0xd1, 0x7a, 0xcf, 0xd4, 0xe1, 0xef, 0x3f, 0x2d, 0xb1, 0x97, 0x71,
+    0x79, 0x7e, 0xde, 0xdb, 0x36, 0x39, 0xeb, 0xdc, 0x79, 0xb7, 0xe9, 0x96, 0xda, 0xb7, 0xd6, 0xc4, 0x7f, 0xaf, 0x4d, 0x32,
+    0x9f, 0x6f, 0xdd, 0x7a, 0xfc, 0x15, 0x37, 0xbd, 0xb8, 0xdd, 0x13, 0xfd, 0xa4, 0x17, 0x23, 0xfb, 0x20, 0xca, 0x23, 0xb5,
+    0xff, 0x71, 0x2f, 0x5a, 0x71, 0x4d, 0xed, 0x95, 0xf6, 0xe7, 0xab, 0x4b, 0xff, 0x1b, 0x6d, 0x78, 0xe6, 0xb5, 0x6b, 0x55,
+    0xbf, 0x1e, 0xf7, 0x1f, 0xbe, 0x5b, 0x2b, 0xdb, 0xf1, 0x72, 0x45, 0xba, 0x2d, 0x62, 0xd8, 0x6b, 0xf6, 0xee, 0x35, 0x23,
+    0x77, 0x76, 0x51, 0x38, 0xef, 0x98, 0x6c, 0xd9, 0x52, 0xa3, 0x5a, 0x5b, 0x3c, 0xfe, 0xaf, 0x88, 0xff, 0xf8, 0x7f, 0x2d,
+    0xee, 0xa9, 0xf1, 0x36, 0x92, 0x65, 0xca, 0x47, 0xed, 0xde, 0x39, 0x6e, 0x7f, 0x3c, 0xbf, 0xba, 0x4e, 0x7b, 0xdb, 0xf6,
+    0x42, 0x6d, 0xf4, 0xb6, 0xb4, 0xee, 0x79, 0x6f, 0xe5, 0xab, 0x5e, 0x24, 0xce, 0x26, 0x4e, 0x5f, 0x4b, 0x63, 0xd0, 0xd2,
+    0xe3, 0xb6, 0x8b, 0x74, 0x49, 0xa3, 0xf0, 0x9d, 0xf1, 0x96, 0x6d, 0xe9, 0x77, 0xae, 0x1b, 0xa3, 0xf2, 0xfb, 0x1b, 0xcf,
+    0xbe, 0xa2, 0x18, 0x0f, 0xe7, 0xcb, 0xb9, 0xa7, 0xaf, 0x89, 0x71, 0xf1, 0x3d, 0x55, 0x36, 0xfe, 0xb7, 0x85, 0xf1, 0x1f,
+    0xe9, 0xfa, 0xbe, 0x2f, 0xfe, 0x9f, 0x18, 0xff, 0xb7, 0x49, 0xfc, 0x6f, 0xe2, 0x3f, 0xf5, 0x7f, 0x73, 0xf7, 0x05, 0xe3,
+    0x71, 0x3b, 0x12, 0xf3, 0x99, 0x58, 0x18, 0xcf, 0x71, 0x7b, 0xf4, 0x8f, 0x7a, 0x5b, 0x65, 0x26, 0xd8, 0xbf, 0xdf, 0xea,
+    0xc7, 0xd9, 0xe8, 0x1e, 0x2d, 0xd2, 0x33, 0xce, 0xd1, 0xdd, 0x55, 0xe6, 0x79, 0x49, 0x2c, 0xeb, 0x99, 0x5b, 0x29, 0xfe,
+    0x23, 0xf5, 0xcc, 0x27, 0x26, 0x35, 0x9f, 0x8d, 0xff, 0x18, 0x5c, 0xff, 0x46, 0xfd, 0x28, 0x2e, 0xbd, 0xff, 0xcf, 0xc6,
+    0xff, 0x36, 0x99, 0x95, 0x6d, 0xa9, 0x9e, 0x5a, 0x29, 0x65, 0x7b, 0xd1, 0x13, 0xd5, 0x95, 0xf1, 0x1f, 0xc9, 0xb3, 0x8e,
+    0x03, 0x4f, 0xc3, 0x7a, 0xc7, 0xa8, 0xdc, 0xbd, 0xb5, 0x93, 0xfd, 0x2a, 0x1e, 0x6c, 0x93, 0x33, 0x23, 0x76, 0x7b, 0x7c,
+    0xfc, 0x8f, 0xc7, 0xe6, 0xff, 0x91, 0x9e, 0x1b, 0x5c, 0x1f, 0xff, 0xf1, 0xf2, 0xe8, 0xaf, 0xcd, 0xff, 0xa3, 0x38, 0x2f,
+    0x8f, 0x45, 0x73, 0xf9, 0xea, 0xdd, 0xcc, 0xbb, 0x5a, 0x64, 0x9b, 0x3e, 0x4f, 0xf8, 0xc4, 0xf9, 0x7f, 0x3c, 0x54, 0x8f,
+    0x51, 0xb8, 0x37, 0x10, 0xff, 0x95, 0xf8, 0xaf, 0xcd, 0x69, 0x67, 0x4f, 0x37, 0x63, 0xe9, 0x95, 0xe9, 0xb3, 0xe3, 0xbf,
+    0x1e, 0xb1, 0xe2, 0xff, 0xe8, 0xdc, 0x36, 0x86, 0xcf, 0x65, 0x63, 0x49, 0x1b, 0x7e, 0xc6, 0x58, 0xb3, 0x2e, 0xfe, 0xe3,
+    0xc4, 0x5d, 0xc3, 0x9f, 0x18, 0xff, 0xdb, 0x8d, 0xf1, 0x1f, 0x5f, 0x3b, 0xff, 0xaf, 0xdc, 0xff, 0xc7, 0xf0, 0x27, 0xd2,
+    0xb1, 0x68, 0x06, 0xf0, 0xfe, 0xd1, 0xbf, 0xf2, 0xb4, 0x67, 0xfc, 0x9b, 0x47, 0x91, 0xfe, 0xb9, 0x40, 0xe5, 0x69, 0xd3,
+    0x6c, 0xee, 0xbc, 0xa5, 0xcb, 0xf6, 0xe9, 0xe3, 0x7f, 0xfe, 0xbc, 0x63, 0xba, 0x25, 0x0a, 0xfd, 0xe8, 0x6d, 0xd7, 0xd7,
+    0x78, 0x7c, 0x64, 0x05, 0xbe, 0x97, 0xf8, 0x07, 0x57, 0x00, 0xd1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xc0, 0x77, 0x6a, 0xff, 0xfa, 0xf7, 0x9f, 0xdb, 0xda, 0x8f, 0xd9, 0x8c, 0x5b, 0x77, 0x4b, 0x6f, 0x5f, 0xad,
+    0x70, 0xfc, 0x7c, 0xc9, 0x56, 0xe7, 0x60, 0xdf, 0x06, 0x67, 0x5a, 0xaf, 0x83, 0xeb, 0xb7, 0x8c, 0x4b, 0x9d, 0x69, 0x9b,
+    0x7e, 0xbd, 0x6c, 0xa9, 0x77, 0x92, 0x73, 0x79, 0x02, 0xdb, 0xe4, 0x0d, 0xd7, 0xec, 0xba, 0x0e, 0xb3, 0x1c, 0x86, 0x99,
+    0xcc, 0x87, 0xed, 0x50, 0x34, 0xcd, 0x3f, 0xcf, 0x96, 0x67, 0x1b, 0x66, 0xc5, 0x8c, 0x72, 0xde, 0xbf, 0x5a, 0xee, 0xe9,
+    0x3d, 0x99, 0xf9, 0x69, 0xf4, 0x3e, 0x7a, 0x4b, 0x67, 0x67, 0xbe, 0x2b, 0x07, 0xfb, 0xa8, 0x3e, 0xf7, 0x74, 0xf6, 0x8b,
+    0xca, 0x96, 0xbd, 0xbb, 0xae, 0xca, 0x2c, 0xd7, 0xc3, 0x7e, 0xb8, 0x15, 0x46, 0x39, 0x92, 0xf7, 0x6e, 0x1e, 0xf1, 0x5c,
+    0x5f, 0x5b, 0x95, 0xfb, 0x3e, 0xa6, 0x25, 0x8e, 0xe4, 0x77, 0xda, 0xf0, 0x38, 0x71, 0xd9, 0x3b, 0x65, 0x91, 0xac, 0xad,
+    0xed, 0xef, 0x1c, 0x1b, 0xb9, 0xba, 0x3c, 0x13, 0xff, 0x95, 0xfc, 0xd2, 0x5b, 0x3a, 0x03, 0xea, 0xde, 0xc9, 0x4b, 0xff,
+    0xfb, 0x7a, 0x76, 0x57, 0xa6, 0x95, 0x6c, 0x5b, 0xdf, 0x11, 0xff, 0xf3, 0xdc, 0x69, 0x91, 0x1a, 0x07, 0xf3, 0x7d, 0xf1,
+    0x89, 0xdc, 0x0d, 0x77, 0xe5, 0x23, 0x3f, 0xd3, 0x8a, 0x57, 0xc6, 0x7f, 0x3e, 0x4f, 0x6c, 0x3d, 0xf3, 0xcf, 0xfa, 0xf8,
+    0xcf, 0xe7, 0xab, 0x8a, 0x6e, 0x3e, 0x15, 0xf1, 0x5f, 0x8d, 0xff, 0xf8, 0x80, 0xf8, 0x5f, 0x99, 0x49, 0x7b, 0xb6, 0xb7,
+    0x48, 0x65, 0x3e, 0x9f, 0x65, 0xf9, 0x69, 0x83, 0xb9, 0xec, 0xb1, 0xbb, 0x8f, 0x38, 0x30, 0xff, 0xb8, 0x27, 0xfe, 0x67,
+    0x99, 0x7b, 0x73, 0xfd, 0xab, 0xba, 0x9f, 0x55, 0x6b, 0x30, 0xcc, 0x7a, 0x4e, 0xb6, 0x45, 0x23, 0xb9, 0x52, 0xe0, 0xea,
+    0xf8, 0x1f, 0x47, 0x6d, 0xad, 0x6c, 0xdb, 0xd2, 0x11, 0xf7, 0xec, 0x08, 0x18, 0x4b, 0xef, 0x0c, 0xee, 0xb9, 0x8a, 0xef,
+    0xa7, 0x57, 0x8f, 0x8b, 0xe1, 0x1d, 0xe6, 0xba, 0xbe, 0x75, 0xef, 0xf8, 0x1f, 0x8b, 0xf3, 0xa9, 0xc7, 0x8d, 0x99, 0x56,
+    0xf3, 0xab, 0x5c, 0x64, 0xeb, 0xb3, 0x92, 0x23, 0x35, 0x9f, 0x55, 0xf9, 0x5b, 0xe2, 0xff, 0xfa, 0x2d, 0xeb, 0x23, 0x2d,
+    0x17, 0xff, 0x71, 0xc3, 0xdc, 0xf2, 0x13, 0xe6, 0xff, 0xdb, 0x30, 0x5f, 0xfd, 0x5d, 0xf1, 0x5f, 0x6d, 0x8d, 0x6b, 0xe3,
+    0x3f, 0x0a, 0xf3, 0xf6, 0x95, 0xf3, 0xfc, 0x67, 0xe6, 0xff, 0xe3, 0x31, 0x26, 0xd2, 0xf5, 0x5e, 0xcd, 0x61, 0x7a, 0x65,
+    0xfc, 0xc7, 0x2d, 0xf7, 0x96, 0xf7, 0xc7, 0xff, 0xda, 0xb8, 0x5c, 0x1b, 0xff, 0x2b, 0xd7, 0xab, 0x10, 0xff, 0xf7, 0xcf,
+    0x0c, 0xd6, 0x7e, 0xa7, 0x72, 0x77, 0xf0, 0xe4, 0xf8, 0x7f, 0x7f, 0x5d, 0x3f, 0x1f, 0xff, 0x2b, 0xe7, 0xff, 0xf1, 0x70,
+    0xfc, 0x57, 0xd6, 0xff, 0xba, 0x67, 0x96, 0x2f, 0xfe, 0x9f, 0x88, 0xff, 0x37, 0xd4, 0x7a, 0x7e, 0xde, 0x34, 0x7f, 0x5a,
+    0x98, 0x5b, 0x35, 0xb1, 0xf6, 0x54, 0x3c, 0x8a, 0x2b, 0x87, 0x47, 0xa1, 0xd4, 0x95, 0x99, 0xe3, 0xb6, 0xe4, 0x5e, 0xbe,
+    0x76, 0x35, 0xa9, 0x64, 0x8e, 0xaf, 0xae, 0x33, 0x76, 0xe7, 0x7d, 0xc1, 0x35, 0xdf, 0xca, 0xb5, 0x62, 0x4c, 0xeb, 0x7e,
+    0xc5, 0x75, 0x64, 0x7b, 0x61, 0x16, 0x7f, 0x3e, 0x5d, 0xdc, 0xf4, 0x1d, 0xd4, 0x22, 0xe2, 0x1f, 0xb5, 0xc8, 0x9b, 0x7a,
+    0x61, 0x24, 0xef, 0xa9, 0xcd, 0x42, 0xd7, 0xd4, 0xba, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x56, 0xfd, 0x7e, 0x72, 0x36, 0xa7, 0xf8, 0x28, 0x6f, 0x62, 0x7e, 0x7f, 0xb3, 0x2c, 0x68, 0xed, 0xc1, 0x2d, 0xa3,
+    0x5a, 0x68, 0x07, 0xcf, 0xa6, 0x25, 0xf3, 0xbc, 0xf5, 0xeb, 0xa4, 0x9f, 0x5f, 0xbe, 0x25, 0xb7, 0xb5, 0xc9, 0x1e, 0x8f,
+    0xee, 0xfd, 0x78, 0x1b, 0xf7, 0xb3, 0xa8, 0xe5, 0xea, 0xbd, 0x9f, 0x99, 0xbf, 0x25, 0x57, 0x2c, 0x98, 0xd5, 0xe8, 0x96,
+    0x68, 0xc9, 0x23, 0xbd, 0x69, 0xb4, 0xd6, 0xc4, 0xd1, 0x76, 0x38, 0x7a, 0x0e, 0x71, 0xf0, 0xad, 0xa3, 0x59, 0xae, 0xc5,
+    0xd1, 0x96, 0x4c, 0x56, 0xa3, 0x33, 0xb9, 0x27, 0x56, 0xe6, 0xd2, 0x5d, 0x95, 0x4b, 0x3f, 0x97, 0xbb, 0x71, 0x1f, 0xb6,
+    0x51, 0x9c, 0xbc, 0x52, 0xb7, 0xc1, 0x31, 0x5a, 0x21, 0xef, 0xc3, 0xf1, 0xbd, 0x1f, 0x6f, 0xe3, 0x3d, 0x9d, 0xdd, 0x64,
+    0x65, 0xfe, 0xfd, 0x96, 0xce, 0xd8, 0xb9, 0x27, 0x57, 0xc4, 0x38, 0x9e, 0x4f, 0x62, 0x4f, 0x67, 0xae, 0x98, 0xf5, 0xc3,
+    0xfd, 0xc4, 0x15, 0xe0, 0xce, 0xfc, 0x3b, 0x6f, 0x88, 0xff, 0x55, 0xb9, 0xb4, 0xaf, 0xce, 0xca, 0xd3, 0xba, 0xa5, 0x6f,
+    0x17, 0xd7, 0x6d, 0x3d, 0xe2, 0x62, 0x98, 0x01, 0x23, 0xca, 0x63, 0xd4, 0xd1, 0x63, 0x8c, 0x22, 0x67, 0xb4, 0x62, 0xd1,
+    0xf1, 0xeb, 0x66, 0x2c, 0x88, 0xff, 0x33, 0x6f, 0x03, 0x1f, 0x1f, 0x5b, 0x8e, 0xd6, 0xef, 0x7d, 0x19, 0xf3, 0xc7, 0x39,
+    0xd8, 0x73, 0x59, 0xd8, 0x57, 0x67, 0xcc, 0x59, 0x17, 0xe7, 0xab, 0xf2, 0xe8, 0xe4, 0xa2, 0x25, 0xd2, 0xf3, 0xf1, 0x4c,
+    0xfc, 0xc7, 0xe1, 0xd9, 0x7f, 0x36, 0x9f, 0x4d, 0x3e, 0x1a, 0x62, 0xf1, 0x1b, 0xb5, 0xfd, 0xf2, 0xb6, 0x8b, 0xc6, 0xff,
+    0xd5, 0xf1, 0xbf, 0x9d, 0x88, 0xfe, 0x7b, 0x57, 0xcc, 0xd8, 0x8a, 0xeb, 0x52, 0x5c, 0x9d, 0x4b, 0x33, 0x0a, 0x65, 0x7b,
+    0x2e, 0xfe, 0xb3, 0x2b, 0x18, 0x8c, 0x8e, 0xd0, 0x0e, 0x97, 0xa4, 0x5d, 0x14, 0x9b, 0x51, 0xec, 0xd5, 0xf1, 0x48, 0x66,
+    0x8e, 0x73, 0xe3, 0xff, 0x76, 0x3a, 0xfe, 0xd7, 0xd7, 0xab, 0xf8, 0xaf, 0xcf, 0x67, 0xee, 0x8d, 0xff, 0xad, 0xb0, 0x4e,
+    0x4b, 0x14, 0x66, 0x5d, 0xf5, 0x2b, 0xc0, 0x5d, 0xfd, 0xfa, 0xed, 0xcf, 0xd3, 0x67, 0xfd, 0xe0, 0x5d, 0xe7, 0x30, 0xeb,
+    0x21, 0x99, 0x7e, 0x15, 0x93, 0xa3, 0xe4, 0xef, 0x8d, 0x2b, 0x7d, 0x7e, 0x2b, 0x66, 0x88, 0xaf, 0xe4, 0x2d, 0x3d, 0x7f,
+    0xbf, 0x70, 0xfe, 0x2a, 0x52, 0x5d, 0xbf, 0x2b, 0x4e, 0xf7, 0x91, 0x4c, 0x7b, 0xad, 0xed, 0xaf, 0xef, 0xbd, 0x0e, 0xcc,
+    0xb3, 0xc6, 0x7e, 0x46, 0x3e, 0xa0, 0xb8, 0xed, 0x5a, 0xfc, 0x96, 0xfa, 0x88, 0x45, 0x77, 0x8e, 0x57, 0x8d, 0xff, 0x6b,
+    0x9f, 0xad, 0x66, 0xe3, 0xe8, 0xfb, 0xb2, 0x58, 0xc5, 0x25, 0xd7, 0x92, 0x58, 0x7a, 0xbf, 0x23, 0xfe, 0xc5, 0xff, 0x15,
+    0xf1, 0x7f, 0xfc, 0x18, 0xf1, 0xb5, 0xf1, 0xff, 0x44, 0x2f, 0x12, 0xff, 0x67, 0xe6, 0x97, 0x6f, 0x9a, 0x51, 0x5e, 0x1b,
+    0xff, 0xeb, 0xc6, 0xb1, 0xca, 0x7d, 0xd7, 0x9b, 0x5a, 0x47, 0xfc, 0xf3, 0x49, 0xed, 0xfc, 0x9e, 0x75, 0x34, 0x00, 0xf1,
+    0x0f, 0x5c, 0x33, 0xef, 0x3e, 0xf6, 0x93, 0x7b, 0xd1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xc0, 0x59, 0xad, 0x9b, 0x59, 0x3c, 0xba, 0x59, 0xcc, 0xff, 0xf9, 0xef, 0xcf, 0x5b, 0x9f, 0x3d, 0x9f, 0x2d, 0x55, 0xb6,
+    0x36, 0xcc, 0xf8, 0xde, 0x1e, 0x2e, 0xf5, 0x95, 0x47, 0x68, 0xb7, 0x9d, 0xe1, 0xda, 0x5a, 0xe9, 0xbd, 0x47, 0xdb, 0x6e,
+    0xa8, 0xc9, 0x5a, 0x7b, 0xb5, 0xe1, 0xb6, 0x96, 0xee, 0xb3, 0xb5, 0x2d, 0xff, 0xfd, 0x9f, 0xfb, 0xb0, 0x86, 0xf7, 0x64,
+    0x5e, 0xde, 0xda, 0x9b, 0xd1, 0xb5, 0x6c, 0x5a, 0x3f, 0x6f, 0xdb, 0xff, 0xfa, 0xdb, 0xdb, 0x96, 0xa9, 0x83, 0x71, 0xee,
+    0xfb, 0x95, 0xe7, 0x33, 0x2a, 0xf5, 0x9a, 0x0c, 0xc4, 0x3f, 0x1f, 0x21, 0x06, 0x67, 0x18, 0xa5, 0x6c, 0x66, 0x77, 0xb5,
+    0x65, 0x6f, 0x4d, 0x86, 0xbd, 0xb0, 0xda, 0xca, 0x1d, 0xed, 0x15, 0xa5, 0xf5, 0x1b, 0xb6, 0x49, 0xdf, 0xcc, 0xf5, 0xe7,
+    0x4c, 0xdd, 0x8f, 0xf2, 0xcd, 0x8e, 0xce, 0xb3, 0x7f, 0xe4, 0xfe, 0x99, 0xec, 0xc9, 0x33, 0xf9, 0xf5, 0xf9, 0x9e, 0xbc,
+    0x96, 0xb6, 0xc1, 0x3c, 0xa7, 0xf2, 0xd6, 0xee, 0xca, 0xf3, 0x19, 0x8d, 0xc3, 0xfd, 0x3a, 0xc8, 0x1f, 0xe3, 0x78, 0x5b,
+    0x8e, 0xea, 0xf8, 0xf9, 0xb6, 0x1c, 0xf5, 0xcc, 0x48, 0x47, 0x52, 0xbe, 0xcc, 0xfd, 0xbe, 0xde, 0xdb, 0xdb, 0x6c, 0x3d,
+    0x9d, 0x67, 0xde, 0x11, 0x9f, 0xc5, 0x7f, 0xa4, 0xe3, 0x7f, 0xbc, 0x0a, 0xcc, 0xf1, 0x4f, 0x47, 0x57, 0xd9, 0x7d, 0x30,
+    0x96, 0xf7, 0x6a, 0xb9, 0x0d, 0xeb, 0xbf, 0xf6, 0xd6, 0xfe, 0xaa, 0xf3, 0xa9, 0x1c, 0x67, 0x4f, 0xd7, 0x59, 0x2e, 0x2b,
+    0xea, 0x3e, 0x3c, 0x93, 0x67, 0xdb, 0x72, 0xd4, 0x9b, 0xdb, 0x20, 0x4b, 0xff, 0xea, 0xf6, 0x8a, 0xc4, 0xde, 0xaa, 0x6b,
+    0xd6, 0x8c, 0xd6, 0xe0, 0xcb, 0x6e, 0xe9, 0x45, 0x79, 0x65, 0xf6, 0x97, 0x9d, 0x13, 0xe7, 0xc7, 0xe5, 0xea, 0x58, 0xbe,
+    0xa5, 0xb3, 0x96, 0xcf, 0x56, 0xc9, 0xc8, 0x8c, 0xd8, 0x95, 0x2d, 0x51, 0xb8, 0x37, 0x6c, 0xe9, 0x27, 0x16, 0xa3, 0x9c,
+    0xe2, 0x91, 0x1c, 0xe3, 0x9e, 0x6d, 0xcb, 0xd9, 0x68, 0x96, 0x39, 0x9b, 0xca, 0x96, 0xd1, 0xac, 0xa9, 0xff, 0x9d, 0xfc,
+    0xca, 0x98, 0x67, 0x56, 0xe7, 0x6b, 0x97, 0xcd, 0xff, 0xe7, 0x39, 0xb6, 0xd7, 0x8c, 0xcb, 0xd5, 0xb1, 0x7c, 0x2b, 0xac,
+    0x5a, 0x10, 0x97, 0xdf, 0x31, 0x56, 0x57, 0x47, 0xdb, 0xbb, 0xf7, 0xc6, 0xb9, 0xcf, 0xab, 0x6b, 0x1f, 0xc4, 0x0b, 0xdb,
+    0x32, 0x1f, 0xff, 0xd7, 0xac, 0x73, 0xb1, 0x76, 0x6d, 0xbc, 0x76, 0x5b, 0x9f, 0x3a, 0x3e, 0xca, 0xaf, 0x5e, 0xe5, 0xee,
+    0xfa, 0x5a, 0xae, 0xe5, 0xf2, 0x7f, 0x3a, 0xfe, 0x63, 0xe1, 0x8a, 0x02, 0xab, 0xe2, 0xbf, 0xbd, 0x24, 0x62, 0xfe, 0x94,
+    0xf8, 0xcf, 0xad, 0xc2, 0x11, 0xa5, 0x95, 0x3b, 0x32, 0x31, 0x9e, 0x7f, 0x96, 0xff, 0x9e, 0x5a, 0xfe, 0x9e, 0xf1, 0xff,
+    0xb9, 0xf8, 0x8f, 0x0f, 0x8c, 0xff, 0xf7, 0x96, 0xf9, 0xf9, 0xb1, 0xf1, 0xd9, 0xf8, 0xaf, 0x8c, 0xcb, 0xf5, 0x75, 0x79,
+    0xd6, 0xc6, 0x7f, 0x24, 0x46, 0xec, 0xf5, 0x99, 0xb8, 0x9f, 0x8a, 0xff, 0xf7, 0x8e, 0xa5, 0xb3, 0xba, 0x7a, 0xb2, 0x64,
+    0xb3, 0x55, 0xe6, 0xdf, 0x15, 0xff, 0xd5, 0x15, 0x55, 0x23, 0x39, 0x93, 0x7d, 0xff, 0xf8, 0x1f, 0xa9, 0x35, 0xf7, 0xbe,
+    0x67, 0xfc, 0x8f, 0xc9, 0xda, 0xbc, 0x9f, 0x37, 0xff, 0x8f, 0x47, 0x63, 0x2c, 0x4a, 0x2b, 0xc9, 0x66, 0x57, 0xda, 0x5b,
+    0x35, 0xff, 0xbf, 0xf3, 0xe7, 0x8c, 0x9f, 0x36, 0x9b, 0x3c, 0xf7, 0xa4, 0xe3, 0x33, 0xe2, 0x7f, 0x3e, 0x92, 0x7e, 0x5e,
+    0x8b, 0x3d, 0x3d, 0xc6, 0xca, 0xf6, 0xfc, 0x4d, 0xf1, 0xbf, 0xdd, 0x32, 0xfe, 0x57, 0x57, 0x3a, 0xcd, 0xfe, 0xfe, 0xd9,
+    0x27, 0xc5, 0xff, 0xca, 0x95, 0x87, 0xee, 0x2c, 0x99, 0xf8, 0x7f, 0xdb, 0x3d, 0xce, 0x8a, 0x92, 0xdf, 0xf5, 0x13, 0x99,
+    0xbb, 0xc7, 0xf9, 0xf7, 0x5e, 0xb1, 0xdf, 0xdb, 0xff, 0xaa, 0xbf, 0xe7, 0x2e, 0xfe, 0x3f, 0x2f, 0xfe, 0xb3, 0x23, 0x76,
+    0x65, 0xcb, 0xfc, 0x48, 0xe7, 0x3f, 0x5f, 0xd7, 0x62, 0xeb, 0x9f, 0x7e, 0xde, 0x71, 0xb7, 0x7a, 0x5f, 0xc9, 0xc4, 0x3f,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xad, 0x36, 0xcc, 0xe4, 0x9f, 0xcd, 0x4a, 0x3e, 0xca, 0x0c,
+    0x18, 0xb7, 0xe4, 0x38, 0xaf, 0xe6, 0xff, 0x3f, 0xfe, 0x79, 0xbe, 0xc6, 0x2a, 0x65, 0x5a, 0x7b, 0xee, 0x6d, 0xd2, 0x66,
+    0x3f, 0x7f, 0xde, 0x0a, 0xab, 0x3c, 0xdc, 0xd1, 0x62, 0xad, 0xd4, 0x9f, 0xf9, 0xa9, 0xb6, 0xf6, 0x85, 0xb5, 0x35, 0x5b,
+    0x33, 0xe0, 0x99, 0x1c, 0xe7, 0xb3, 0xe3, 0xb4, 0x45, 0xc7, 0x18, 0x67, 0xb8, 0xce, 0x95, 0xa9, 0x72, 0xee, 0xeb, 0xdb,
+    0xb2, 0x5f, 0x8a, 0x7b, 0x5a, 0xac, 0xf9, 0x0d, 0xfb, 0x47, 0xdf, 0xd9, 0xa9, 0x64, 0x18, 0x8e, 0x64, 0xf6, 0xa3, 0xfb,
+    0x4a, 0xbd, 0xee, 0x08, 0xfd, 0xab, 0xd8, 0xfe, 0xd2, 0xb1, 0x67, 0x9c, 0x7b, 0x76, 0x94, 0x15, 0xb1, 0xdd, 0xf4, 0x66,
+    0x58, 0x4b, 0xbf, 0xcb, 0xd3, 0x16, 0xed, 0x4d, 0x94, 0xaf, 0xca, 0x3d, 0xbc, 0x4d, 0xf3, 0x72, 0xad, 0xba, 0xce, 0xe4,
+    0x4b, 0xbd, 0x0d, 0xf6, 0x95, 0xfb, 0x7c, 0x4b, 0xe6, 0x6a, 0x89, 0xc5, 0x59, 0xdc, 0xd7, 0xd6, 0xca, 0x36, 0xb9, 0x8e,
+    0x47, 0x22, 0xc6, 0xce, 0x6c, 0xc9, 0x1f, 0xe7, 0x6d, 0x6b, 0x69, 0x7c, 0xee, 0x7b, 0xb9, 0xfb, 0xa2, 0xdc, 0xc3, 0xf5,
+    0x4c, 0x9a, 0xab, 0x72, 0x9c, 0xd7, 0x32, 0xb3, 0xac, 0xcb, 0xa4, 0xb7, 0x77, 0xfb, 0xf1, 0xda, 0x33, 0xbc, 0xbe, 0x56,
+    0x3e, 0x21, 0x97, 0xee, 0x1b, 0xdf, 0x0c, 0xff, 0xec, 0xf7, 0xf3, 0x8f, 0x8c, 0x67, 0xdb, 0x74, 0xce, 0xf8, 0x64, 0xfb,
+    0x5f, 0x9f, 0x49, 0x67, 0xfb, 0xc8, 0x0c, 0x87, 0x5b, 0x39, 0x03, 0x98, 0xf8, 0xff, 0xf6, 0xf9, 0x7f, 0xdc, 0x90, 0x17,
+    0x69, 0x76, 0x2d, 0x59, 0x9b, 0xff, 0x77, 0x13, 0xff, 0x27, 0xa3, 0xe2, 0xf9, 0x5c, 0x9a, 0xf3, 0xab, 0xfc, 0x26, 0xfe,
+    0x17, 0x8d, 0xfb, 0xdf, 0x96, 0x49, 0xd3, 0xf8, 0x7f, 0x5d, 0x54, 0x7c, 0xea, 0x5a, 0x1a, 0x6c, 0x85, 0xb1, 0x7c, 0x65,
+    0x86, 0xe1, 0xb8, 0xe1, 0x0a, 0x54, 0xc9, 0xcc, 0x98, 0xff, 0xfc, 0xd3, 0xa2, 0x7c, 0x65, 0x8e, 0xad, 0x6b, 0xca, 0xb6,
+    0x26, 0xfe, 0xdf, 0x99, 0xfd, 0xfa, 0x13, 0xe7, 0xff, 0xf7, 0x3c, 0x63, 0xd8, 0x6e, 0xce, 0xcc, 0x16, 0x4b, 0x3e, 0xdf,
+    0x92, 0x77, 0x38, 0x2b, 0xef, 0x97, 0x9e, 0xcf, 0xa4, 0x77, 0x5f, 0x56, 0xfa, 0xda, 0x5d, 0x9e, 0xf1, 0x1f, 0xbe, 0xfb,
+    0xd9, 0xb4, 0xf8, 0x07, 0xf1, 0x6f, 0xf6, 0x0f, 0xee, 0x4c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xc7, 0xaf, 0x3f, 0xea, 0x01, 0xc4, 0x3f, 0xf0, 0xc7,
+    0xc5, 0xff, 0xff, 0x00 };
+
+// Font glyphs rectangles data (on atlas)
+static const Rectangle amberFontRecs[189] = {
+    { 4, 4, 5 , 16 },
+    { 17, 4, 2 , 10 },
+    { 27, 4, 4 , 4 },
+    { 39, 4, 5 , 10 },
+    { 52, 4, 5 , 11 },
+    { 65, 4, 5 , 10 },
+    { 78, 4, 5 , 10 },
+    { 91, 4, 2 , 4 },
+    { 101, 4, 4 , 13 },
+    { 113, 4, 5 , 13 },
+    { 126, 4, 4 , 4 },
+    { 138, 4, 5 , 6 },
+    { 151, 4, 3 , 2 },
+    { 162, 4, 5 , 2 },
+    { 175, 4, 2 , 1 },
+    { 185, 4, 5 , 10 },
+    { 198, 4, 5 , 10 },
+    { 211, 4, 4 , 10 },
+    { 223, 4, 5 , 10 },
+    { 236, 4, 5 , 10 },
+    { 249, 4, 5 , 10 },
+    { 262, 4, 5 , 10 },
+    { 275, 4, 5 , 10 },
+    { 288, 4, 5 , 10 },
+    { 301, 4, 5 , 10 },
+    { 314, 4, 5 , 10 },
+    { 327, 4, 2 , 6 },
+    { 337, 4, 2 , 6 },
+    { 347, 4, 5 , 6 },
+    { 360, 4, 5 , 4 },
+    { 373, 4, 5 , 6 },
+    { 386, 4, 5 , 10 },
+    { 399, 4, 5 , 7 },
+    { 412, 4, 5 , 10 },
+    { 425, 4, 5 , 10 },
+    { 438, 4, 5 , 10 },
+    { 451, 4, 5 , 10 },
+    { 464, 4, 5 , 10 },
+    { 477, 4, 5 , 10 },
+    { 490, 4, 5 , 10 },
+    { 4, 28, 5 , 10 },
+    { 17, 28, 4 , 10 },
+    { 29, 28, 5 , 10 },
+    { 42, 28, 5 , 10 },
+    { 55, 28, 5 , 10 },
+    { 68, 28, 5 , 10 },
+    { 81, 28, 5 , 10 },
+    { 94, 28, 5 , 10 },
+    { 107, 28, 5 , 10 },
+    { 120, 28, 5 , 10 },
+    { 133, 28, 5 , 10 },
+    { 146, 28, 5 , 10 },
+    { 159, 28, 5 , 10 },
+    { 172, 28, 5 , 10 },
+    { 185, 28, 5 , 10 },
+    { 198, 28, 5 , 10 },
+    { 211, 28, 5 , 10 },
+    { 224, 28, 5 , 10 },
+    { 237, 28, 5 , 10 },
+    { 250, 28, 3 , 13 },
+    { 261, 28, 5 , 10 },
+    { 274, 28, 3 , 13 },
+    { 285, 28, 4 , 3 },
+    { 297, 28, 5 , 1 },
+    { 310, 28, 3 , 3 },
+    { 321, 28, 5 , 7 },
+    { 334, 28, 5 , 10 },
+    { 347, 28, 5 , 7 },
+    { 360, 28, 5 , 10 },
+    { 373, 28, 5 , 7 },
+    { 386, 28, 5 , 10 },
+    { 399, 28, 5 , 10 },
+    { 412, 28, 5 , 10 },
+    { 425, 28, 4 , 10 },
+    { 437, 28, 3 , 13 },
+    { 448, 28, 5 , 10 },
+    { 461, 28, 5 , 10 },
+    { 474, 28, 5 , 7 },
+    { 487, 28, 5 , 7 },
+    { 4, 52, 5 , 7 },
+    { 17, 52, 5 , 10 },
+    { 30, 52, 5 , 10 },
+    { 43, 52, 5 , 7 },
+    { 56, 52, 5 , 7 },
+    { 69, 52, 5 , 10 },
+    { 82, 52, 5 , 7 },
+    { 95, 52, 5 , 7 },
+    { 108, 52, 5 , 7 },
+    { 121, 52, 5 , 7 },
+    { 134, 52, 5 , 10 },
+    { 147, 52, 5 , 7 },
+    { 160, 52, 4 , 13 },
+    { 172, 52, 2 , 13 },
+    { 182, 52, 4 , 13 },
+    { 194, 52, 5 , 4 },
+    { 207, 52, 2 , 9 },
+    { 217, 52, 5 , 7 },
+    { 230, 52, 5 , 10 },
+    { 243, 52, 5 , 10 },
+    { 256, 52, 5 , 10 },
+    { 269, 52, 0 , 0 },
+    { 277, 52, 5 , 10 },
+    { 290, 52, 0 , 0 },
+    { 298, 52, 5 , 7 },
+    { 311, 52, 3 , 5 },
+    { 322, 52, 5 , 5 },
+    { 335, 52, 5 , 3 },
+    { 348, 52, 5 , 7 },
+    { 361, 52, 5 , 2 },
+    { 374, 52, 4 , 4 },
+    { 386, 52, 5 , 8 },
+    { 399, 52, 3 , 5 },
+    { 410, 52, 3 , 6 },
+    { 421, 52, 0 , 0 },
+    { 429, 52, 5 , 10 },
+    { 442, 52, 5 , 10 },
+    { 455, 52, 2 , 3 },
+    { 465, 52, 0 , 0 },
+    { 473, 52, 3 , 5 },
+    { 484, 52, 4 , 4 },
+    { 496, 52, 5 , 5 },
+    { 4, 76, 5 , 10 },
+    { 17, 76, 5 , 7 },
+    { 30, 76, 5 , 10 },
+    { 43, 76, 5 , 10 },
+    { 56, 76, 5 , 14 },
+    { 69, 76, 5 , 14 },
+    { 82, 76, 5 , 14 },
+    { 95, 76, 5 , 14 },
+    { 108, 76, 5 , 12 },
+    { 121, 76, 5 , 12 },
+    { 134, 76, 5 , 10 },
+    { 147, 76, 5 , 13 },
+    { 160, 76, 5 , 14 },
+    { 173, 76, 5 , 14 },
+    { 186, 76, 5 , 14 },
+    { 199, 76, 5 , 12 },
+    { 212, 76, 4 , 14 },
+    { 224, 76, 4 , 14 },
+    { 236, 76, 4 , 14 },
+    { 248, 76, 4 , 12 },
+    { 260, 76, 5 , 10 },
+    { 273, 76, 5 , 14 },
+    { 286, 76, 5 , 14 },
+    { 299, 76, 5 , 14 },
+    { 312, 76, 5 , 14 },
+    { 325, 76, 5 , 14 },
+    { 338, 76, 5 , 12 },
+    { 351, 76, 4 , 3 },
+    { 363, 76, 5 , 10 },
+    { 376, 76, 5 , 14 },
+    { 389, 76, 5 , 14 },
+    { 402, 76, 5 , 14 },
+    { 415, 76, 5 , 12 },
+    { 428, 76, 5 , 14 },
+    { 441, 76, 5 , 10 },
+    { 454, 76, 5 , 10 },
+    { 467, 76, 5 , 10 },
+    { 480, 76, 5 , 10 },
+    { 493, 76, 5 , 10 },
+    { 4, 100, 5 , 10 },
+    { 17, 100, 5 , 9 },
+    { 30, 100, 5 , 9 },
+    { 43, 100, 5 , 7 },
+    { 56, 100, 5 , 10 },
+    { 69, 100, 5 , 10 },
+    { 82, 100, 5 , 10 },
+    { 95, 100, 5 , 10 },
+    { 108, 100, 5 , 9 },
+    { 121, 100, 4 , 10 },
+    { 133, 100, 4 , 10 },
+    { 145, 100, 4 , 10 },
+    { 157, 100, 4 , 9 },
+    { 169, 100, 5 , 10 },
+    { 182, 100, 5 , 10 },
+    { 195, 100, 5 , 10 },
+    { 208, 100, 5 , 10 },
+    { 221, 100, 5 , 10 },
+    { 234, 100, 5 , 10 },
+    { 247, 100, 5 , 9 },
+    { 260, 100, 5 , 6 },
+    { 273, 100, 5 , 7 },
+    { 286, 100, 5 , 10 },
+    { 299, 100, 5 , 10 },
+    { 312, 100, 5 , 10 },
+    { 325, 100, 5 , 9 },
+    { 338, 100, 5 , 13 },
+    { 351, 100, 5 , 10 },
+    { 364, 100, 5 , 12 },
+};
+
+// Font glyphs info data
+// NOTE: No glyphs.image data provided
+static const GlyphInfo amberFontGlyphs[189] = {
+    { 32, 0, 0, 5, { 0 }},
+    { 33, 1, 3, 5, { 0 }},
+    { 34, 0, 3, 5, { 0 }},
+    { 35, 0, 3, 5, { 0 }},
+    { 36, 0, 3, 5, { 0 }},
+    { 37, 0, 3, 5, { 0 }},
+    { 38, 0, 3, 5, { 0 }},
+    { 39, 1, 4, 5, { 0 }},
+    { 40, 0, 3, 5, { 0 }},
+    { 41, 0, 3, 5, { 0 }},
+    { 42, 0, 3, 5, { 0 }},
+    { 43, 0, 7, 5, { 0 }},
+    { 44, 0, 12, 5, { 0 }},
+    { 45, 0, 9, 5, { 0 }},
+    { 46, 1, 12, 5, { 0 }},
+    { 47, 0, 3, 5, { 0 }},
+    { 48, 0, 3, 5, { 0 }},
+    { 49, 0, 3, 5, { 0 }},
+    { 50, 0, 3, 5, { 0 }},
+    { 51, 0, 3, 5, { 0 }},
+    { 52, 0, 3, 5, { 0 }},
+    { 53, 0, 3, 5, { 0 }},
+    { 54, 0, 3, 5, { 0 }},
+    { 55, 0, 3, 5, { 0 }},
+    { 56, 0, 3, 5, { 0 }},
+    { 57, 0, 3, 5, { 0 }},
+    { 58, 0, 7, 5, { 0 }},
+    { 59, 0, 7, 5, { 0 }},
+    { 60, 0, 7, 5, { 0 }},
+    { 61, 0, 8, 5, { 0 }},
+    { 62, 0, 7, 5, { 0 }},
+    { 63, 0, 3, 5, { 0 }},
+    { 64, 0, 6, 5, { 0 }},
+    { 65, 0, 3, 5, { 0 }},
+    { 66, 0, 3, 5, { 0 }},
+    { 67, 0, 3, 5, { 0 }},
+    { 68, 0, 3, 5, { 0 }},
+    { 69, 0, 3, 5, { 0 }},
+    { 70, 0, 3, 5, { 0 }},
+    { 71, 0, 3, 5, { 0 }},
+    { 72, 0, 3, 5, { 0 }},
+    { 73, 0, 3, 5, { 0 }},
+    { 74, 0, 3, 5, { 0 }},
+    { 75, 0, 3, 5, { 0 }},
+    { 76, 0, 3, 5, { 0 }},
+    { 77, 0, 3, 5, { 0 }},
+    { 78, 0, 3, 5, { 0 }},
+    { 79, 0, 3, 5, { 0 }},
+    { 80, 0, 3, 5, { 0 }},
+    { 81, 0, 3, 5, { 0 }},
+    { 82, 0, 3, 5, { 0 }},
+    { 83, 0, 3, 5, { 0 }},
+    { 84, 0, 3, 5, { 0 }},
+    { 85, 0, 3, 5, { 0 }},
+    { 86, 0, 3, 5, { 0 }},
+    { 87, 0, 3, 5, { 0 }},
+    { 88, 0, 3, 5, { 0 }},
+    { 89, 0, 3, 5, { 0 }},
+    { 90, 0, 3, 5, { 0 }},
+    { 91, 0, 3, 5, { 0 }},
+    { 92, 0, 3, 5, { 0 }},
+    { 93, 0, 3, 5, { 0 }},
+    { 94, 0, 3, 5, { 0 }},
+    { 95, 0, 12, 5, { 0 }},
+    { 96, 1, 4, 5, { 0 }},
+    { 97, 0, 6, 5, { 0 }},
+    { 98, 0, 3, 5, { 0 }},
+    { 99, 0, 6, 5, { 0 }},
+    { 100, 0, 3, 5, { 0 }},
+    { 101, 0, 6, 5, { 0 }},
+    { 102, 0, 3, 5, { 0 }},
+    { 103, 0, 6, 5, { 0 }},
+    { 104, 0, 3, 5, { 0 }},
+    { 105, 0, 3, 5, { 0 }},
+    { 106, 0, 3, 5, { 0 }},
+    { 107, 0, 3, 5, { 0 }},
+    { 108, 0, 3, 5, { 0 }},
+    { 109, 0, 6, 5, { 0 }},
+    { 110, 0, 6, 5, { 0 }},
+    { 111, 0, 6, 5, { 0 }},
+    { 112, 0, 6, 5, { 0 }},
+    { 113, 0, 6, 5, { 0 }},
+    { 114, 0, 6, 5, { 0 }},
+    { 115, 0, 6, 5, { 0 }},
+    { 116, 0, 3, 5, { 0 }},
+    { 117, 0, 6, 5, { 0 }},
+    { 118, 0, 6, 5, { 0 }},
+    { 119, 0, 6, 5, { 0 }},
+    { 120, 0, 6, 5, { 0 }},
+    { 121, 0, 6, 5, { 0 }},
+    { 122, 0, 6, 5, { 0 }},
+    { 123, 0, 3, 5, { 0 }},
+    { 124, 1, 3, 5, { 0 }},
+    { 125, 0, 3, 5, { 0 }},
+    { 126, 0, 8, 5, { 0 }},
+    { 161, 1, 4, 5, { 0 }},
+    { 162, 0, 6, 5, { 0 }},
+    { 163, 0, 3, 5, { 0 }},
+    { 8364, 0, 3, 5, { 0 }},
+    { 165, 0, 3, 5, { 0 }},
+    { 352, 0, 0, 0, { 0 }},
+    { 167, 0, 3, 5, { 0 }},
+    { 353, 0, 0, 0, { 0 }},
+    { 169, 0, 6, 5, { 0 }},
+    { 170, 2, 3, 5, { 0 }},
+    { 171, 0, 8, 5, { 0 }},
+    { 172, 0, 6, 5, { 0 }},
+    { 174, 0, 6, 5, { 0 }},
+    { 175, 0, 3, 5, { 0 }},
+    { 176, 1, 3, 5, { 0 }},
+    { 177, 0, 5, 5, { 0 }},
+    { 178, 2, 3, 5, { 0 }},
+    { 179, 2, 3, 5, { 0 }},
+    { 381, 0, 0, 0, { 0 }},
+    { 181, 0, 6, 5, { 0 }},
+    { 182, 0, 3, 5, { 0 }},
+    { 183, 1, 6, 5, { 0 }},
+    { 382, 0, 0, 0, { 0 }},
+    { 185, 0, 3, 5, { 0 }},
+    { 186, 0, 3, 5, { 0 }},
+    { 187, 0, 8, 5, { 0 }},
+    { 338, 0, 3, 5, { 0 }},
+    { 339, 0, 6, 5, { 0 }},
+    { 376, 0, 3, 5, { 0 }},
+    { 191, 0, 3, 5, { 0 }},
+    { 192, 0, -1, 5, { 0 }},
+    { 193, 0, -1, 5, { 0 }},
+    { 194, 0, -1, 5, { 0 }},
+    { 195, 0, -1, 5, { 0 }},
+    { 196, 0, 1, 5, { 0 }},
+    { 197, 0, 1, 5, { 0 }},
+    { 198, 0, 3, 5, { 0 }},
+    { 199, 0, 3, 5, { 0 }},
+    { 200, 0, -1, 5, { 0 }},
+    { 201, 0, -1, 5, { 0 }},
+    { 202, 0, -1, 5, { 0 }},
+    { 203, 0, 1, 5, { 0 }},
+    { 204, 0, -1, 5, { 0 }},
+    { 205, 0, -1, 5, { 0 }},
+    { 206, 0, -1, 5, { 0 }},
+    { 207, 0, 1, 5, { 0 }},
+    { 208, 0, 3, 5, { 0 }},
+    { 209, 0, -1, 5, { 0 }},
+    { 210, 0, -1, 5, { 0 }},
+    { 211, 0, -1, 5, { 0 }},
+    { 212, 0, -1, 5, { 0 }},
+    { 213, 0, -1, 5, { 0 }},
+    { 214, 0, 1, 5, { 0 }},
+    { 215, 0, 10, 5, { 0 }},
+    { 216, 0, 3, 5, { 0 }},
+    { 217, 0, -1, 5, { 0 }},
+    { 218, 0, -1, 5, { 0 }},
+    { 219, 0, -1, 5, { 0 }},
+    { 220, 0, 1, 5, { 0 }},
+    { 221, 0, -1, 5, { 0 }},
+    { 222, 0, 3, 5, { 0 }},
+    { 223, 0, 3, 5, { 0 }},
+    { 224, 0, 3, 5, { 0 }},
+    { 225, 0, 3, 5, { 0 }},
+    { 226, 0, 3, 5, { 0 }},
+    { 227, 0, 3, 5, { 0 }},
+    { 228, 0, 4, 5, { 0 }},
+    { 229, 0, 4, 5, { 0 }},
+    { 230, 0, 6, 5, { 0 }},
+    { 231, 0, 6, 5, { 0 }},
+    { 232, 0, 3, 5, { 0 }},
+    { 233, 0, 3, 5, { 0 }},
+    { 234, 0, 3, 5, { 0 }},
+    { 235, 0, 4, 5, { 0 }},
+    { 236, 0, 3, 5, { 0 }},
+    { 237, 0, 3, 5, { 0 }},
+    { 238, 0, 3, 5, { 0 }},
+    { 239, 0, 4, 5, { 0 }},
+    { 240, 0, 3, 5, { 0 }},
+    { 241, 0, 3, 5, { 0 }},
+    { 242, 0, 3, 5, { 0 }},
+    { 243, 0, 3, 5, { 0 }},
+    { 244, 0, 3, 5, { 0 }},
+    { 245, 0, 3, 5, { 0 }},
+    { 246, 0, 4, 5, { 0 }},
+    { 247, 0, 7, 5, { 0 }},
+    { 248, 0, 6, 5, { 0 }},
+    { 249, 0, 3, 5, { 0 }},
+    { 250, 0, 3, 5, { 0 }},
+    { 251, 0, 3, 5, { 0 }},
+    { 252, 0, 4, 5, { 0 }},
+    { 253, 0, 3, 5, { 0 }},
+    { 254, 0, 3, 5, { 0 }},
+    { 255, 0, 4, 5, { 0 }},
+};
+
+// Style loading function: Amber
+static void GuiLoadStyleAmber(void)
+{
+    // Load style properties provided
+    // NOTE: Default properties are propagated
+    for (int i = 0; i < AMBER_STYLE_PROPS_COUNT; i++)
+    {
+        GuiSetStyle(amberStyleProps[i].controlId, amberStyleProps[i].propertyId, amberStyleProps[i].propertyValue);
+    }
+
+    // Custom font loading
+    // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function
+    int amberFontDataSize = 0;
+    unsigned char *data = DecompressData(amberFontData, AMBER_STYLE_FONT_ATLAS_COMP_SIZE, &amberFontDataSize);
+    Image imFont = { data, 512, 256, 1, 2 };
+
+    Font font = { 0 };
+    font.baseSize = 16;
+    font.glyphCount = 189;
+
+    // Load texture from image
+    font.texture = LoadTextureFromImage(imFont);
+    UnloadImage(imFont);  // Uncompressed image data can be unloaded from memory
+
+    // Copy char recs data from global fontRecs
+    // NOTE: Required to avoid issues if trying to free font
+    font.recs = (Rectangle *)RAYGUI_MALLOC(font.glyphCount*sizeof(Rectangle));
+    memcpy(font.recs, amberFontRecs, font.glyphCount*sizeof(Rectangle));
+
+    // Copy font char info data from global fontChars
+    // NOTE: Required to avoid issues if trying to free font
+    font.glyphs = (GlyphInfo *)RAYGUI_MALLOC(font.glyphCount*sizeof(GlyphInfo));
+    memcpy(font.glyphs, amberFontGlyphs, font.glyphCount*sizeof(GlyphInfo));
+
+    GuiSetFont(font);
+
+    // Setup a white rectangle on the font to be used on shapes drawing,
+    // it makes possible to draw shapes and text (full UI) in a single draw call
+    Rectangle fontWhiteRec = { 510, 254, 1, 1 };
     SetShapesTexture(font.texture, fontWhiteRec);
 
     //-----------------------------------------------------------------
diff --git a/raylib/examples/audio/audio_mixed_processor.c b/raylib/examples/audio/audio_mixed_processor.c
--- a/raylib/examples/audio/audio_mixed_processor.c
+++ b/raylib/examples/audio/audio_mixed_processor.c
@@ -97,7 +97,7 @@
             DrawRectangle(199, 199, 402, 34, LIGHTGRAY);
             for (int i = 0; i < 400; i++)
             {
-                DrawLine(201 + i, 232 - (int)averageVolume[i] * 32, 201 + i, 232, MAROON);
+                DrawLine(201 + i, 232 - (int)(averageVolume[i] * 32), 201 + i, 232, MAROON);
             }
             DrawRectangleLines(199, 199, 402, 34, GRAY);
 
diff --git a/raylib/examples/core/core_automation_events.c b/raylib/examples/core/core_automation_events.c
--- a/raylib/examples/core/core_automation_events.c
+++ b/raylib/examples/core/core_automation_events.c
@@ -88,7 +88,7 @@
         // Update
         //----------------------------------------------------------------------------------
         float deltaTime = 0.015f;//GetFrameTime();
-        
+       
         // Dropped files logic
         //----------------------------------------------------------------------------------
         if (IsFileDropped())
@@ -157,11 +157,6 @@
         }
         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))
         {
             // Reset game state
@@ -176,12 +171,44 @@
         }
         //----------------------------------------------------------------------------------
 
+        // Events playing
+        // NOTE: Logic must be before Camera update because it depends on mouse-wheel value, 
+        // that can be set by the played event... but some other inputs could be affected
+        //----------------------------------------------------------------------------------
+        if (eventPlaying)
+        {
+            // NOTE: Multiple events could be executed in a single frame
+            while (playFrameCounter == aelist.events[currentPlayFrame].frame)
+            {
+                PlayAutomationEvent(aelist.events[currentPlayFrame]);
+                currentPlayFrame++;
+
+                if (currentPlayFrame == aelist.count)
+                {
+                    eventPlaying = false;
+                    currentPlayFrame = 0;
+                    playFrameCounter = 0;
+
+                    TraceLog(LOG_INFO, "FINISH PLAYING!");
+                    break;
+                }
+            }
+
+            playFrameCounter++;
+        }
+        //----------------------------------------------------------------------------------
+
         // Update camera
         //----------------------------------------------------------------------------------
         camera.target = player.position;
         camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
         float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000;
 
+        // WARNING: On event replay, mouse-wheel internal value is set
+        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;
+
         for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
         {
             EnvElement *element = &envElements[i];
@@ -200,8 +227,8 @@
         if (min.y > 0) camera.offset.y = screenHeight/2 - min.y;
         //----------------------------------------------------------------------------------
         
-        // Toggle events recording
-        if (IsKeyPressed(KEY_S))
+        // Events management
+        if (IsKeyPressed(KEY_S))    // Toggle events recording
         {
             if (!eventPlaying)
             {
@@ -222,7 +249,7 @@
                 }
             }
         }
-        else if (IsKeyPressed(KEY_A))
+        else if (IsKeyPressed(KEY_A)) // Toggle events playing (WARNING: Starts next frame)
         {
             if (!eventRecording && (aelist.count > 0))
             {
@@ -241,32 +268,7 @@
                 camera.zoom = 1.0f;
             }
         }
-        
-        if (eventPlaying)
-        {
-            // NOTE: Multiple events could be executed in a single frame
-            while (playFrameCounter == aelist.events[currentPlayFrame].frame)
-            {
-                TraceLog(LOG_INFO, "PLAYING: PlayFrameCount: %i | currentPlayFrame: %i | Event Frame: %i, param: %i", 
-                    playFrameCounter, currentPlayFrame, aelist.events[currentPlayFrame].frame, aelist.events[currentPlayFrame].params[0]);
-                
-                PlayAutomationEvent(aelist.events[currentPlayFrame]);
-                currentPlayFrame++;
 
-                if (currentPlayFrame == aelist.count)
-                {
-                    eventPlaying = false;
-                    currentPlayFrame = 0;
-                    playFrameCounter = 0;
-
-                    TraceLog(LOG_INFO, "FINISH PLAYING!");
-                    break;
-                }
-            }
-            
-            playFrameCounter++;
-        }
-        
         if (eventRecording || eventPlaying) frameCounter++;
         else frameCounter = 0;
         //----------------------------------------------------------------------------------
diff --git a/raylib/examples/core/core_input_gamepad_info.c b/raylib/examples/core/core_input_gamepad_info.c
--- a/raylib/examples/core/core_input_gamepad_info.c
+++ b/raylib/examples/core/core_input_gamepad_info.c
@@ -47,25 +47,25 @@
 
             ClearBackground(RAYWHITE);
 
-            for (int i = 0, y = 10; i < 4; i++)     // MAX_GAMEPADS = 4
+            for (int i = 0, y = 5; i < 4; i++)     // MAX_GAMEPADS = 4
             {
                 if (IsGamepadAvailable(i))
                 {
-                    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;
+                    DrawText(TextFormat("Gamepad name: %s", GetGamepadName(i)), 10, y, 10, BLACK);
+                    y += 11;
+                    DrawText(TextFormat("\tAxis count:   %d", GetGamepadAxisCount(i)), 10, y, 10, BLACK);
+                    y += 11;
 
                     for (int axis = 0; axis < GetGamepadAxisCount(i); axis++)
                     {
-                        DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 20, BLACK);
-                        y += 30;
+                        DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 10, BLACK);
+                        y += 11;
                     }
 
                     for (int button = 0; button < 32; button++)
                     {
-                        DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 20, BLACK);
-                        y += 30;
+                        DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 10, BLACK);
+                        y += 11;
                     }
                 }
             }
diff --git a/raylib/examples/core/core_input_virtual_controls.c b/raylib/examples/core/core_input_virtual_controls.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_input_virtual_controls.c
@@ -0,0 +1,123 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - input virtual controls
+*
+*   Example originally created with raylib 5.0, last time updated with raylib 5.0
+*
+*   Example create by GreenSnakeLinux (@GreenSnakeLinux),
+*   lighter by oblerion (@oblerion) 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) 2024 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include <math.h>
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - input virtual controls");
+
+    const int dpadX = 90;
+    const int dpadY = 300;
+    const int dpadRad = 25;//radius of each pad
+    Color dpadColor = BLUE;
+    int dpadKeydown = -1;//-1 if not down, else 0,1,2,3 
+
+    
+    const float dpadCollider[4][2]= // collider array with x,y position
+    {
+        {dpadX,dpadY-dpadRad*1.5},//up
+        {dpadX-dpadRad*1.5,dpadY},//left
+        {dpadX+dpadRad*1.5,dpadY},//right
+        {dpadX,dpadY+dpadRad*1.5}//down
+    };
+    const char dpadLabel[4]="XYBA";//label of Dpad
+
+    float playerX=100;
+    float playerY=100;
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+	// Update 
+	//--------------------------------------------------------------------------
+        dpadKeydown = -1; //reset
+        float inputX=0;
+        float inputY=0;
+        if(GetTouchPointCount()>0)
+        {//use touch pos
+            inputX = GetTouchX();
+            inputY = GetTouchY();
+        }
+        else
+        {//use mouse pos
+            inputX = GetMouseX();
+            inputY = GetMouseY();
+        }
+        for(int i=0;i<4;i++)
+        {
+            //test distance each collider and input < radius
+            if( fabsf(dpadCollider[i][1]-inputY) + fabsf(dpadCollider[i][0]-inputX) < dpadRad)
+            {
+                dpadKeydown = i;
+                break;
+            }
+        }
+        // move player
+        switch(dpadKeydown){
+            case 0: playerY -= 50*GetFrameTime();
+            break;
+            case 1: playerX -= 50*GetFrameTime();
+            break;
+            case 2: playerX += 50*GetFrameTime();
+            break;
+            case 3: playerY += 50*GetFrameTime();
+            default:;
+        };
+	//--------------------------------------------------------------------------
+	    
+	// Draw 
+	//--------------------------------------------------------------------------
+       BeginDrawing();
+            ClearBackground(RAYWHITE);
+            for(int i=0;i<4;i++)
+            {
+                //draw all pad
+                DrawCircle(dpadCollider[i][0],dpadCollider[i][1],dpadRad,dpadColor);
+                if(i!=dpadKeydown)
+                {
+                    //draw label
+                    DrawText(TextSubtext(dpadLabel,i,1),
+                             dpadCollider[i][0]-5,
+                             dpadCollider[i][1]-5,16,BLACK);
+                }
+
+            }
+            DrawText("Player",playerX,playerY,16,BLACK);
+        EndDrawing();
+	//--------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
diff --git a/raylib/examples/core/core_vr_simulator.c b/raylib/examples/core/core_vr_simulator.c
--- a/raylib/examples/core/core_vr_simulator.c
+++ b/raylib/examples/core/core_vr_simulator.c
@@ -100,7 +100,7 @@
 
     DisableCursor();                    // Limit cursor to relative movement inside the window
 
-    SetTargetFPS(90);                   // Set our game to run at 90 frames-per-second
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
 
     // Main game loop
diff --git a/raylib/examples/models/models_box_collisions.c b/raylib/examples/models/models_box_collisions.c
--- a/raylib/examples/models/models_box_collisions.c
+++ b/raylib/examples/models/models_box_collisions.c
@@ -109,7 +109,7 @@
 
             EndMode3D();
 
-            DrawText("Move player with cursors to collide", 220, 40, 20, GRAY);
+            DrawText("Move player with arrow keys to collide", 220, 40, 20, GRAY);
 
             DrawFPS(10, 10);
 
diff --git a/raylib/examples/models/models_gpu_skinning.c b/raylib/examples/models/models_gpu_skinning.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_gpu_skinning.c
@@ -0,0 +1,119 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - Doing skinning on the gpu using a vertex shader
+* 
+*   Example originally created with raylib 4.5, last time updated with raylib 4.5
+*
+*   Example contributed by Daniel Holden (@orangeduck) 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) 2024 Daniel Holden (@orangeduck)
+* 
+*   Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - GPU skinning");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
+    camera.target = (Vector3){ 0.0f, 2.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
+
+    // Load gltf model
+    Model characterModel = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
+    
+    // Load skinning shader
+    Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
+                                       TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
+    
+    characterModel.materials[1].shader = skinningShader;
+    
+    // Load gltf model animations
+    int animsCount = 0;
+    unsigned int animIndex = 0;
+    unsigned int animCurrentFrame = 0;
+    ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
+
+    Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
+
+    DisableCursor();                    // Limit cursor to relative movement inside the window
+
+    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_THIRD_PERSON);
+        
+        // Select current animation
+        if (IsKeyPressed(KEY_T)) animIndex = (animIndex + 1)%animsCount;
+        else if (IsKeyPressed(KEY_G)) animIndex = (animIndex + animsCount - 1)%animsCount;
+
+        // Update model animation
+        ModelAnimation anim = modelAnimations[animIndex];
+        animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
+        UpdateModelAnimationBoneMatrices(characterModel, anim, animCurrentFrame);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+            
+                // Draw character
+                characterModel.transform = MatrixTranslate(position.x, position.y, position.z);
+                UpdateModelAnimationBoneMatrices(characterModel, anim, animCurrentFrame);
+                DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
+
+                DrawGrid(10, 1.0f);
+            EndMode3D();
+
+            DrawText("Use the T/G to switch animation", 10, 10, 20, GRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadModelAnimations(modelAnimations, animsCount);
+    UnloadModel(characterModel);         // Unload character model and meshes/material
+    UnloadShader(skinningShader);
+    
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_point_rendering.c b/raylib/examples/models/models_point_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_point_rendering.c
@@ -0,0 +1,182 @@
+/*******************************************************************************************
+*
+*   raylib example - point rendering
+*
+*   Example originally created with raylib 5.0, last time updated with raylib 5.0
+*
+*   Example contributed by Reese Gallagher (@satchelfrost) 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) 2024 Reese Gallagher (@satchelfrost)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>             // Required for: rand()
+#include <math.h>               // Required for: cos(), sin()
+
+#define MAX_POINTS 10000000     // 10 million
+#define MIN_POINTS 1000         // 1 thousand
+
+// Generate mesh using points
+Mesh GenMeshPoints(int numPoints);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main()
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+    
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - point rendering");
+
+    Camera camera = {
+        .position   = { 3.0f, 3.0f, 3.0f },
+        .target     = { 0.0f, 0.0f, 0.0f },
+        .up         = { 0.0f, 1.0f, 0.0f },
+        .fovy       = 45.0f,
+        .projection = CAMERA_PERSPECTIVE
+    };
+
+    Vector3 position = { 0.0f, 0.0f, 0.0f };
+    bool useDrawModelPoints = true;
+    bool numPointsChanged = false;
+    int numPoints = 1000;
+    
+    Mesh mesh = GenMeshPoints(numPoints);
+    Model model = LoadModelFromMesh(mesh);
+    
+    //SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while(!WindowShouldClose())
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+
+        if (IsKeyPressed(KEY_SPACE)) useDrawModelPoints = !useDrawModelPoints;
+        if (IsKeyPressed(KEY_UP))
+        {
+            numPoints = (numPoints*10 > MAX_POINTS)? MAX_POINTS : numPoints*10;
+            numPointsChanged = true;
+        }
+        if (IsKeyPressed(KEY_DOWN))
+        {
+            numPoints = (numPoints/10 < MIN_POINTS)? MIN_POINTS : numPoints/10;
+            numPointsChanged = true;
+        }
+
+        // Upload a different point cloud size
+        if (numPointsChanged)
+        {
+            UnloadModel(model);
+            mesh = GenMeshPoints(numPoints);
+            model = LoadModelFromMesh(mesh);
+            numPointsChanged = false;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(BLACK);
+
+            BeginMode3D(camera);
+
+                // The new method only uploads the points once to the GPU
+                if (useDrawModelPoints)
+                {
+                    DrawModelPoints(model, position, 1.0f, WHITE);
+                }
+                else
+                {
+                    // The old method must continually draw the "points" (lines)
+                    for (int i = 0; i < numPoints; i++)
+                    {
+                        Vector3 pos = {
+                            .x = mesh.vertices[i*3 + 0],
+                            .y = mesh.vertices[i*3 + 1],
+                            .z = mesh.vertices[i*3 + 2],
+                        };
+                        Color color = {
+                            .r = mesh.colors[i*4 + 0],
+                            .g = mesh.colors[i*4 + 1],
+                            .b = mesh.colors[i*4 + 2],
+                            .a = mesh.colors[i*4 + 3],
+                        };
+                        
+                        DrawPoint3D(pos, color);
+                    }
+                }
+
+                // Draw a unit sphere for reference
+                DrawSphereWires(position, 1.0f, 10, 10, YELLOW);
+                
+            EndMode3D();
+
+            // Draw UI text
+            DrawText(TextFormat("Point Count: %d", numPoints), 20, screenHeight - 50, 40, WHITE);
+            DrawText("Up - increase points", 20, 70, 20, WHITE);
+            DrawText("Down - decrease points", 20, 100, 20, WHITE);
+            DrawText("Space - drawing function", 20, 130, 20, WHITE);
+            
+            if (useDrawModelPoints) DrawText("Using: DrawModelPoints()", 20, 160, 20, GREEN);
+            else DrawText("Using: DrawPoint3D()", 20, 160, 20, RED);
+            
+            DrawFPS(10, 10);
+            
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadModel(model);
+
+    CloseWindow();
+    //--------------------------------------------------------------------------------------
+    return 0;
+}
+
+// Generate a spherical point cloud
+Mesh GenMeshPoints(int numPoints)
+{
+    Mesh mesh = { 
+        .triangleCount = 1,
+        .vertexCount = numPoints,
+        .vertices = (float *)MemAlloc(numPoints*3*sizeof(float)),
+        .colors = (unsigned char*)MemAlloc(numPoints*4*sizeof(unsigned char)),
+    };
+
+    // https://en.wikipedia.org/wiki/Spherical_coordinate_system
+    for (int i = 0; i < numPoints; i++)
+    {
+        float theta = PI*rand()/RAND_MAX;
+        float phi = 2.0f*PI*rand()/RAND_MAX;
+        float r = 10.0f*rand()/RAND_MAX;
+        
+        mesh.vertices[i*3 + 0] = r*sin(theta)*cos(phi);
+        mesh.vertices[i*3 + 1] = r*sin(theta)*sin(phi);
+        mesh.vertices[i*3 + 2] = r*cos(theta);
+        
+        Color color = ColorFromHSV(r*360.0f, 1.0f, 1.0f);
+        
+        mesh.colors[i*4 + 0] = color.r;
+        mesh.colors[i*4 + 1] = color.g;
+        mesh.colors[i*4 + 2] = color.b;
+        mesh.colors[i*4 + 3] = color.a;
+    }
+
+    // Upload mesh data from CPU (RAM) to GPU (VRAM) memory
+    UploadMesh(&mesh, false);
+    
+    return mesh;
+}
diff --git a/raylib/examples/models/models_yaw_pitch_roll.c b/raylib/examples/models/models_yaw_pitch_roll.c
--- a/raylib/examples/models/models_yaw_pitch_roll.c
+++ b/raylib/examples/models/models_yaw_pitch_roll.c
@@ -114,6 +114,7 @@
     // De-Initialization
     //--------------------------------------------------------------------------------------
     UnloadModel(model);     // Unload model data
+    UnloadTexture(texture); // Unload texture data
 
     CloseWindow();          // Close window and OpenGL context
     //--------------------------------------------------------------------------------------
diff --git a/raylib/examples/shaders/shaders_basic_pbr.c b/raylib/examples/shaders/shaders_basic_pbr.c
--- a/raylib/examples/shaders/shaders_basic_pbr.c
+++ b/raylib/examples/shaders/shaders_basic_pbr.c
@@ -236,7 +236,7 @@
                 float emissiveIntensity = 0.01f;
                 SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, SHADER_UNIFORM_FLOAT);
                 
-                DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.005f, WHITE);   // Draw car model
+                DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.25f, WHITE);   // Draw car model
 
                 // Draw spheres to show the lights positions
                 for (int i = 0; i < MAX_LIGHTS; i++)
diff --git a/raylib/examples/shaders/shaders_lightmap.c b/raylib/examples/shaders/shaders_lightmap.c
--- a/raylib/examples/shaders/shaders_lightmap.c
+++ b/raylib/examples/shaders/shaders_lightmap.c
@@ -164,6 +164,8 @@
     //--------------------------------------------------------------------------------------
     UnloadMesh(mesh);       // Unload the mesh
     UnloadShader(shader);   // Unload shader
+    UnloadTexture(texture); // Unload texture
+    UnloadTexture(light);   // Unload texture
 
     CloseWindow();          // Close window and OpenGL context
     //--------------------------------------------------------------------------------------
diff --git a/raylib/examples/shaders/shaders_texture_drawing.c b/raylib/examples/shaders/shaders_texture_drawing.c
--- a/raylib/examples/shaders/shaders_texture_drawing.c
+++ b/raylib/examples/shaders/shaders_texture_drawing.c
@@ -77,6 +77,7 @@
     // De-Initialization
     //--------------------------------------------------------------------------------------
     UnloadShader(shader);
+    UnloadTexture(texture);
 
     CloseWindow();        // Close window and OpenGL context
     //--------------------------------------------------------------------------------------
diff --git a/raylib/examples/shaders/shaders_vertex_displacement.c b/raylib/examples/shaders/shaders_vertex_displacement.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_vertex_displacement.c
@@ -0,0 +1,119 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - Vertex displacement
+*
+*   Example originally created with raylib 5.0, last time updated with raylib 4.5
+*
+*   Example contributed by <Alex ZH> (@ZzzhHe) 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 <Alex ZH> (@ZzzhHe)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.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
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - vertex displacement");
+
+    // set up camera
+    Camera camera = {0};
+    camera.position = (Vector3) {20.0f, 5.0f, -20.0f};
+    camera.target = (Vector3) {0.0f, 0.0f, 0.0f};
+    camera.up = (Vector3) {0.0f, 1.0f, 0.0f};
+    camera.fovy = 60.0f;
+    camera.projection = CAMERA_PERSPECTIVE;
+
+    // Load vertex and fragment shaders
+    Shader shader = LoadShader(
+        TextFormat("resources/shaders/glsl%i/vertex_displacement.vs", GLSL_VERSION),
+        TextFormat("resources/shaders/glsl%i/vertex_displacement.fs", GLSL_VERSION));
+    
+    // Load perlin noise texture
+    Image perlinNoiseImage = GenImagePerlinNoise(512, 512, 0, 0, 1.0f);
+    Texture perlinNoiseMap = LoadTextureFromImage(perlinNoiseImage);
+    UnloadImage(perlinNoiseImage);
+
+    // Set shader uniform location
+    int perlinNoiseMapLoc = GetShaderLocation(shader, "perlinNoiseMap");
+    rlEnableShader(shader.id);
+    rlActiveTextureSlot(1);
+    rlEnableTexture(perlinNoiseMap.id);
+    rlSetUniformSampler(perlinNoiseMapLoc, 1);
+    
+    // Create a plane mesh and model
+    Mesh planeMesh = GenMeshPlane(50, 50, 50, 50);
+    Model planeModel = LoadModelFromMesh(planeMesh);
+    // Set plane model material
+    planeModel.materials[0].shader = shader;
+
+    float time = 0.0f;
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_FREE); // Update camera
+
+        time += GetFrameTime(); // Update time variable
+        SetShaderValue(shader, GetShaderLocation(shader, "time"), &time, SHADER_UNIFORM_FLOAT); // Send time value to shader
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                BeginShaderMode(shader);
+                    // Draw plane model
+                    DrawModel(planeModel, (Vector3){ 0.0f, 0.0f, 0.0f }, 1.0f, (Color) {255, 255, 255, 255});
+                EndShaderMode();
+
+            EndMode3D();
+
+            DrawText("Vertex displacement", 10, 10, 20, DARKGRAY);
+            DrawFPS(10, 40);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shader);
+    UnloadModel(planeModel);
+    UnloadTexture(perlinNoiseMap);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/raygui.h b/raylib/examples/shapes/raygui.h
--- a/raylib/examples/shapes/raygui.h
+++ b/raylib/examples/shapes/raygui.h
@@ -1,6 +1,6 @@
 /*******************************************************************************************
 *
-*   raygui v4.0 - A simple and easy-to-use immediate-mode gui library
+*   raygui v4.5-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
@@ -26,7 +26,7 @@
 *   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
+*         no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
 *         by default any previously loaded font (texture, recs, glyphs).
 *       - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
 *
@@ -141,6 +141,24 @@
 *           Draw text bounds rectangles for debug
 *
 *   VERSIONS HISTORY:
+*       4.5-dev (Sep-2024)    Current dev version...
+*                         ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
+*                         ADDED: GuiValueBoxFloat()
+*                         ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
+*                         ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+*                         ADDED: Multiple new icons
+*                         REVIEWED: GuiTabBar(), close tab with mouse middle button
+*                         REVIEWED: GuiScrollPanel(), scroll speed proportional to content
+*                         REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
+*                         REVIEWED: GuiTextBox(), cursor position initialization
+*                         REVIEWED: GuiSliderPro(), control value change check
+*                         REVIEWED: GuiGrid(), simplified implementation
+*                         REVIEWED: GuiIconText(), increase buffer size and reviewed padding
+*                         REVIEWED: GuiDrawText(), improved wrap mode drawing
+*                         REVIEWED: GuiScrollBar(), minor tweaks
+*                         REVIEWED: Functions descriptions, removed wrong return value reference
+*                         REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
+*
 *       4.0 (12-Sep-2023) ADDED: GuiToggleSlider()
 *                         ADDED: GuiColorPickerHSV() and GuiColorPanelHSV()
 *                         ADDED: Multiple new icons, mostly compiler related
@@ -314,9 +332,9 @@
 #define RAYGUI_H
 
 #define RAYGUI_VERSION_MAJOR 4
-#define RAYGUI_VERSION_MINOR 0
+#define RAYGUI_VERSION_MINOR 5
 #define RAYGUI_VERSION_PATCH 0
-#define RAYGUI_VERSION  "4.0"
+#define RAYGUI_VERSION  "4.5-dev"
 
 #if !defined(RAYGUI_STANDALONE)
     #include "raylib.h"
@@ -441,7 +459,6 @@
     } Font;
 #endif
 
-
 // Style property
 // NOTE: Used when exporting style as code for convenience
 typedef struct GuiStyleProp {
@@ -546,7 +563,6 @@
 // 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)
 //----------------------------------------------------------------------------------
@@ -562,12 +578,12 @@
     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
+    //TEXT_DECORATION_THICK       // Text decoration line thickness
 } GuiDefaultProperty;
 
 // Other possible text properties:
 // TEXT_WEIGHT                  // Normal, Italic, Bold -> Requires specific font change
-// TEXT_INDENT	                // Text indentation -> Now using TEXT_PADDING...
+// TEXT_INDENT                  // Text indentation -> Now using TEXT_PADDING...
 
 // Label
 //typedef enum { } GuiLabelProperty;
@@ -615,7 +631,9 @@
 // DropdownBox
 typedef enum {
     ARROW_PADDING = 16,         // DropdownBox arrow separation from border and items
-    DROPDOWN_ITEMS_SPACING      // DropdownBox items separation
+    DROPDOWN_ITEMS_SPACING,     // DropdownBox items separation
+    DROPDOWN_ARROW_HIDDEN,      // DropdownBox arrow hidden
+    DROPDOWN_ROLL_UP            // DropdownBox roll up flag (default rolls down)
 } GuiDropdownBoxProperty;
 
 // TextBox/TextBoxMulti/ValueBox/Spinner
@@ -635,6 +653,7 @@
     LIST_ITEMS_SPACING,         // ListView items separation
     SCROLLBAR_WIDTH,            // ListView scrollbar size (usually width)
     SCROLLBAR_SIDE,             // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+    LIST_ITEMS_BORDER_WIDTH     // ListView items border width
 } GuiListViewProperty;
 
 // ColorPicker
@@ -698,7 +717,6 @@
 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
@@ -710,29 +728,30 @@
 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 GuiLabel(Rectangle bounds, const char *text);                                            // Label control
 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 GuiToggleSlider(Rectangle bounds, const char *text, int *active);                        // Toggle Slider control, returns true when clicked
+RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text);                                      // Label button control, returns true when clicked
+RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active);                             // Toggle Button control
+RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active);                         // Toggle Group control
+RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active);                        // Toggle Slider control
 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 GuiComboBox(Rectangle bounds, const char *text, int *active);                            // Combo Box control
 
-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 GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode);          // Dropdown Box control
+RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control
 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 GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values
 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 GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control
+RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control
+RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control
 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
+RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control
 
 // 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 GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active);          // List View control
 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
@@ -741,10 +760,9 @@
 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()
+RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv);                 // Color Panel control that updates Hue-Saturation-Value color value, used by GuiColorPickerHSV()
 //----------------------------------------------------------------------------------------------------------
 
-
 #if !defined(RAYGUI_NO_ICONS)
 
 #if !defined(RAYGUI_CUSTOM_ICONS)
@@ -972,14 +990,14 @@
     ICON_FOLDER                   = 217,
     ICON_FILE                     = 218,
     ICON_SAND_TIMER               = 219,
-    ICON_220                      = 220,
-    ICON_221                      = 221,
-    ICON_222                      = 222,
-    ICON_223                      = 223,
-    ICON_224                      = 224,
-    ICON_225                      = 225,
-    ICON_226                      = 226,
-    ICON_227                      = 227,
+    ICON_WARNING                  = 220,
+    ICON_HELP_BOX                 = 221,
+    ICON_INFO_BOX                 = 222,
+    ICON_PRIORITY                 = 223,
+    ICON_LAYERS_ISO               = 224,
+    ICON_LAYERS2                  = 225,
+    ICON_MLAYERS                  = 226,
+    ICON_MAPS                     = 227,
     ICON_228                      = 228,
     ICON_229                      = 229,
     ICON_230                      = 230,
@@ -1290,14 +1308,14 @@
     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
+    0x01800000, 0x02400240, 0x05a00420, 0x09900990, 0x11881188, 0x21842004, 0x40024182, 0x00003ffc,      // ICON_WARNING
+    0x7ffe0000, 0x4ff24002, 0x4c324ff2, 0x4f824c02, 0x41824f82, 0x41824002, 0x40024182, 0x00007ffe,      // ICON_HELP_BOX
+    0x7ffe0000, 0x41824002, 0x40024182, 0x41824182, 0x41824182, 0x41824182, 0x40024182, 0x00007ffe,      // ICON_INFO_BOX
+    0x01800000, 0x04200240, 0x10080810, 0x7bde2004, 0x0a500a50, 0x08500bd0, 0x08100850, 0x00000ff0,      // ICON_PRIORITY
+    0x01800000, 0x18180660, 0x80016006, 0x98196006, 0x99996666, 0x19986666, 0x01800660, 0x00000000,      // ICON_LAYERS_ISO
+    0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0,      // ICON_LAYERS2
+    0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0,      // ICON_MLAYERS
+    0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0,      // ICON_MAPS
     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
@@ -1328,7 +1346,7 @@
     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
+// NOTE: A pointer to current icons array should be defined
 static unsigned int *guiIconsPtr = guiIcons;
 
 #endif      // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS
@@ -1363,8 +1381,8 @@
 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 bool guiControlExclusiveMode = false;    // Gui control exclusive mode (no inputs processed except current control)
+static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier
 
 static int textBoxCursorIndex = 0;              // Cursor index, shared by all GuiTextBox*()
 //static int blinkCursorFrameCounter = 0;       // Frame counter for cursor blinking
@@ -1450,6 +1468,7 @@
 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 float TextToFloat(const char *text);         // Get float value from 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)
@@ -1744,6 +1763,9 @@
                 if (toggle) *active = i;
             }
 
+            // Close tab with middle mouse button pressed
+            if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i;
+
             GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding);
             GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment);
 
@@ -1775,10 +1797,10 @@
 {
     #define RAYGUI_MIN_SCROLLBAR_WIDTH     40
     #define RAYGUI_MIN_SCROLLBAR_HEIGHT    40
+    #define RAYGUI_MIN_MOUSE_WHEEL_SPEED   20
 
     int result = 0;
     GuiState state = guiState;
-    float mouseWheelSpeed = 20.0f;      // Default movement speed with mouse wheel
 
     Rectangle temp = { 0 };
     if (view == NULL) view = &temp;
@@ -1820,17 +1842,8 @@
     };
 
     // 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
-    }
+    if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH;
+    if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT;
 
     // Calculate view area (area without the scrollbars)
     *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)?
@@ -1873,9 +1886,14 @@
 #endif
             float wheelMove = GetMouseWheelMove();
 
+            // Set scrolling speed with mouse wheel based on ratio between bounds and content
+            Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height };
+            if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+            if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+
             // 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
+            if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x;
+            else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll
         }
     }
 
@@ -1921,7 +1939,7 @@
     }
 
     // Draw scrollbar lines depending on current state
-    GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK);
+    GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, 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);
@@ -1959,7 +1977,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -1997,7 +2015,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -2020,7 +2038,7 @@
     return pressed;
 }
 
-// Toggle Button control, returns true when active
+// Toggle Button control
 int GuiToggle(Rectangle bounds, const char *text, bool *active)
 {
     int result = 0;
@@ -2031,7 +2049,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -2068,7 +2086,7 @@
     return result;
 }
 
-// Toggle Group control, returns toggled button codepointIndex
+// Toggle Group control
 int GuiToggleGroup(Rectangle bounds, const char *text, int *active)
 {
     #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS)
@@ -2117,7 +2135,7 @@
     return result;
 }
 
-// Toggle Slider control extended, returns true when clicked
+// Toggle Slider control extended
 int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
 {
     int result = 0;
@@ -2211,7 +2229,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -2256,7 +2274,7 @@
     return result;
 }
 
-// Combo Box control, returns selected item codepointIndex
+// Combo Box control
 int GuiComboBox(Rectangle bounds, const char *text, int *active)
 {
     int result = 0;
@@ -2279,7 +2297,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -2327,21 +2345,28 @@
     int result = 0;
     GuiState state = guiState;
 
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
     int itemSelected = *active;
     int itemFocused = -1;
 
+    int direction = 0; // Dropdown box open direction: down (default)
+    if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up
+
     // Get substrings items from text (items pointers, lengths and count)
     int itemCount = 0;
     const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
 
     Rectangle boundsOpen = bounds;
     boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+    if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING);
 
     Rectangle itemBounds = bounds;
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -2362,7 +2387,8 @@
             for (int i = 0; i < itemCount; i++)
             {
                 // Update item rectangle y position for next item
-                itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+                if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+                else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
 
                 if (CheckCollisionPointRec(mousePoint, itemBounds))
                 {
@@ -2406,7 +2432,8 @@
         for (int i = 0; i < itemCount; i++)
         {
             // Update item rectangle y position for next item
-            itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+            if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+            else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
 
             if (i == itemSelected)
             {
@@ -2422,14 +2449,17 @@
         }
     }
 
-    // Draw arrows (using icon if available)
+    if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN))
+    {
+        // 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, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+        GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
+            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, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));   // ICON_ARROW_DOWN_FILL
+        GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));   // ICON_ARROW_DOWN_FILL
 #endif
+    }
     //--------------------------------------------------------------------
 
     *active = itemSelected;
@@ -2440,7 +2470,7 @@
 
 // Text Box control
 // NOTE: Returns true on ENTER pressed (useful for data validation)
-int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
+int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
 {
     #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
         #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN  40        // Frames to wait for autocursor movement
@@ -2456,7 +2486,10 @@
     int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
 
     Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
-    int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex);
+    int textLength = (int)strlen(text);     // Get current text length
+    int thisCursorIndex = textBoxCursorIndex;
+    if (thisCursorIndex > textLength) thisCursorIndex = textLength;
+    int textWidth = GetTextWidth(text) - GetTextWidth(text + thisCursorIndex);
     int textIndexOffset = 0;    // Text index offset to start drawing in the box
 
     // Cursor rectangle
@@ -2496,7 +2529,7 @@
     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
+        !guiControlExclusiveMode &&                       // No gui slider on dragging
         (wrapMode == TEXT_WRAP_NONE))               // No wrap mode
     {
         Vector2 mousePosition = GetMousePosition();
@@ -2505,6 +2538,8 @@
         {
             state = STATE_PRESSED;
 
+            if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
+
             // 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)
@@ -2517,19 +2552,16 @@
                 textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex);
             }
 
-            int textLength = (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))
+            if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
             {
                 // Move forward data from cursor position
                 for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
@@ -2564,6 +2596,7 @@
                     for (int i = textBoxCursorIndex; i < textLength; i++) text[i] = text[i + nextCodepointSize];
 
                     textLength -= codepointSize;
+                    if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
 
                     // Make sure text last character is EOL
                     text[textLength] = '\0';
@@ -2583,6 +2616,8 @@
                     // Move backward text from cursor position
                     for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
 
+                    // TODO Check: >= cursor+codepointsize and <= length-codepointsize
+
                     // Prevent cursor index from decrementing past 0
                     if (textBoxCursorIndex > 0)
                     {
@@ -2653,7 +2688,7 @@
                 if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
                 {
                     mouseCursor.x = textBounds.x + textEndWidth;
-                    mouseCursorIndex = (int)strlen(text);
+                    mouseCursorIndex = textLength;
                 }
 
                 // Place cursor at required index on mouse click
@@ -2685,7 +2720,7 @@
 
                 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
                 {
-                    textBoxCursorIndex = (int)strlen(text);   // GLOBAL: Place cursor index to the end of current text
+                    textBoxCursorIndex = textLength;   // GLOBAL: Place cursor index to the end of current text
                     result = 1;
                 }
             }
@@ -2727,7 +2762,7 @@
 /*
 // 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 GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode)
 {
     bool pressed = false;
 
@@ -2736,7 +2771,7 @@
     GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP);
 
     // TODO: Implement methods to calculate cursor position properly
-    pressed = GuiTextBox(bounds, text, bufferSize, editMode);
+    pressed = GuiTextBox(bounds, text, textSize, editMode);
 
     GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE);
     GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE);
@@ -2771,7 +2806,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -2846,7 +2881,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -2890,7 +2925,13 @@
             //if (*value > maxValue) *value = maxValue;
             //else if (*value < minValue) *value = minValue;
 
-            if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1;
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
+            {
+                if (*value > maxValue) *value = maxValue;
+                else if (*value < minValue) *value = minValue;
+
+                result = 1;
+            }
         }
         else
         {
@@ -2930,55 +2971,152 @@
     return result;
 }
 
+// Floating point Value Box control, updates input val_str with numbers
+// NOTE: Requires static variables: frameCounter
+int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode)
+{
+    #if !defined(RAYGUI_VALUEBOX_MAX_CHARS)
+        #define RAYGUI_VALUEBOX_MAX_CHARS  32
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
+    //sprintf(textValue, "%2.2f", *value);
+
+    Rectangle textBounds = {0};
+    if (text != NULL)
+    {
+        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;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        bool valueHasChanged = false;
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            int keyCount = (int)strlen(textValue);
+
+            // Only allow keys in range [48..57]
+            if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+            {
+                if (GetTextWidth(textValue) < bounds.width)
+                {
+                    int key = GetCharPressed();
+                    if (((key >= 48) && (key <= 57)) ||
+                        (key == '.') ||
+                        ((keyCount == 0) && (key == '+')) ||  // NOTE: Sign can only be in first position
+                        ((keyCount == 0) && (key == '-')))
+                    {
+                        textValue[keyCount] = (char)key;
+                        keyCount++;
+
+                        valueHasChanged = true;
+                    }
+                }
+            }
+
+            // Pressed backspace
+            if (IsKeyPressed(KEY_BACKSPACE))
+            {
+                if (keyCount > 0)
+                {
+                    keyCount--;
+                    textValue[keyCount] = '\0';
+                    valueHasChanged = true;
+                }
+            }
+
+            if (valueHasChanged) *value = TextToFloat(textValue);
+
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1;
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                state = STATE_FOCUSED;
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    Color baseColor = BLANK;
+    if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
+    else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
+
+    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, 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,
+                GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
 // Slider control with pro parameters
 // NOTE: Other GuiSlider*() controls use this one
 int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth)
 {
     int result = 0;
-    float oldValue = *value;
     GuiState state = guiState;
 
     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)));
+    float oldValue = *value;
 
     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) };
 
-    if (sliderWidth > 0)        // Slider
-    {
-        slider.x += (sliderValue - sliderWidth/2);
-        slider.width = (float)sliderWidth;
-    }
-    else if (sliderWidth == 0)  // SliderBar
-    {
-        slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
-        slider.width = (float)sliderValue;
-    }
-
     // Update control
     //--------------------------------------------------------------------
     if ((state != STATE_DISABLED) && !guiLocked)
     {
         Vector2 mousePoint = GetMousePosition();
 
-        if (guiSliderDragging) // Keep dragging outside of bounds
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
         {
             if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
             {
-                if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
                 {
                     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;
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
                 }
             }
             else
             {
-                guiSliderDragging = false;
-                guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
             }
         }
         else if (CheckCollisionPointRec(mousePoint, bounds))
@@ -2986,16 +3124,13 @@
             if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
             {
                 state = STATE_PRESSED;
-                guiSliderDragging = true;
-                guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
 
                 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
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
                 }
             }
             else state = STATE_FOCUSED;
@@ -3006,17 +3141,22 @@
     }
 
     // Control value change check
-    if(oldValue == *value) result = 0;
+    if (oldValue == *value) result = 0;
     else result = 1;
 
-    // Bar limits check
+    // Slider bar limits check
+    float sliderValue = (((*value - minValue)/(maxValue - minValue))*(bounds.width - sliderWidth - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
     if (sliderWidth > 0)        // Slider
     {
+        slider.x += sliderValue;
+        slider.width = (float)sliderWidth;
         if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH);
         else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH);
     }
     else if (sliderWidth == 0)  // SliderBar
     {
+        slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
+        slider.width = sliderValue;
         if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
     }
     //--------------------------------------------------------------------
@@ -3171,7 +3311,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -3238,7 +3378,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
 
@@ -3291,6 +3431,8 @@
     // Draw visible items
     for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
     {
+        GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+
         if (state == STATE_DISABLED)
         {
             if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
@@ -3353,83 +3495,32 @@
     return result;
 }
 
-// Color Panel control
+// Color Panel control - Color (RGBA) variant.
 int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
 {
     int result = 0;
-    GuiState state = guiState;
-    Vector2 pickerSelector = { 0 };
 
-    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
-    pickerSelector.y = bounds.y + (1.0f - (float)hsv.z)*bounds.height;  // HSV: Value
-
-    Vector3 maxHue = { hsv.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
-
-                hsv.y = colorPick.x;
-                hsv.z = 1.0f - colorPick.y;
-
-                Vector3 rgb = ConvertHSVtoRGB(hsv);
-
-                // NOTE: Vector3ToColor() only available on raylib 1.8.1
-                *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) };
+    Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv.
 
-            }
-            else state = STATE_FOCUSED;
-        }
-    }
-    //--------------------------------------------------------------------
+    GuiColorPanelHSV(bounds, text, &hsv);
 
-    // Draw control
-    //--------------------------------------------------------------------
-    if (state != STATE_DISABLED)
+    // Check if the hsv was changed, only then change the color.
+    // This is required, because the Color->HSV->Color conversion has precision errors.
+    // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value.
+    // Otherwise GuiColorPanel would often modify it's color without user input.
+    // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check.
+    if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
     {
-        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));
+        Vector3 rgb = ConvertHSVtoRGB(hsv);
 
-        // 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));
+        // NOTE: Vector3ToColor() only available on raylib 1.8.1
+        *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x),
+                            (unsigned char)(255.0f*rgb.y),
+                            (unsigned char)(255.0f*rgb.z),
+                            color->a };
     }
-
-    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
-    //--------------------------------------------------------------------
-
     return result;
 }
 
@@ -3451,11 +3542,11 @@
     {
         Vector2 mousePoint = GetMousePosition();
 
-        if (guiSliderDragging) // Keep dragging outside of bounds
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
         {
             if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
             {
-                if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
                 {
                     state = STATE_PRESSED;
 
@@ -3466,8 +3557,8 @@
             }
             else
             {
-                guiSliderDragging = false;
-                guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
             }
         }
         else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
@@ -3475,8 +3566,8 @@
             if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
             {
                 state = STATE_PRESSED;
-                guiSliderDragging = true;
-                guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
 
                 *alpha = (mousePoint.x - bounds.x)/bounds.width;
                 if (*alpha <= 0.0f) *alpha = 0.0f;
@@ -3537,11 +3628,11 @@
     {
         Vector2 mousePoint = GetMousePosition();
 
-        if (guiSliderDragging) // Keep dragging outside of bounds
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
         {
             if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
             {
-                if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
                 {
                     state = STATE_PRESSED;
 
@@ -3552,8 +3643,8 @@
             }
             else
             {
-                guiSliderDragging = false;
-                guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
             }
         }
         else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
@@ -3561,8 +3652,8 @@
             if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
             {
                 state = STATE_PRESSED;
-                guiSliderDragging = true;
-                guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
 
                 *hue = (mousePoint.y - bounds.y)*360/bounds.height;
                 if (*hue <= 0.0f) *hue = 0.0f;
@@ -3615,6 +3706,7 @@
 //      float GuiColorBarAlpha(Rectangle bounds, float alpha)
 //      float GuiColorBarHue(Rectangle bounds, float value)
 // NOTE: bounds define GuiColorPanel() size
+// NOTE: this picker converts RGB to HSV, which can cause the Hue control to jump. If you have this problem, consider using the HSV variant instead
 int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
 {
     int result = 0;
@@ -3627,6 +3719,7 @@
     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) };
 
+    // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used.
     Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
 
     GuiColorBarHue(boundsHue, NULL, &hsv.x);
@@ -3668,8 +3761,7 @@
     return result;
 }
 
-// Color Panel control, returns HSV color value in *colorHsv.
-// Used by GuiColorPickerHSV()
+// Color Panel control - HSV variant
 int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
 {
     int result = 0;
@@ -3690,15 +3782,47 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked)
     {
         Vector2 mousePoint = GetMousePosition();
 
-        if (CheckCollisionPointRec(mousePoint, bounds))
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
         {
             if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
             {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    pickerSelector = mousePoint;
+
+                    if (pickerSelector.x < bounds.x) pickerSelector.x = bounds.x;
+                    if (pickerSelector.x > bounds.x + bounds.width) pickerSelector.x = bounds.x + bounds.width;
+                    if (pickerSelector.y < bounds.y) pickerSelector.y = bounds.y;
+                    if (pickerSelector.y > bounds.y + bounds.height) pickerSelector.y = bounds.y + bounds.height;
+
+                    // 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
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
                 state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds;
                 pickerSelector = mousePoint;
 
                 // Calculate color from picker
@@ -3760,9 +3884,9 @@
     int textWidth = GetTextWidth(message) + 2;
 
     Rectangle textBounds = { 0 };
-    textBounds.x = bounds.x + bounds.width/2 - textWidth/2;
+    textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
     textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
-    textBounds.width = (float)textWidth;
+    textBounds.width = bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*2;
     textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
 
     // Draw control
@@ -3905,7 +4029,7 @@
 
     // Update control
     //--------------------------------------------------------------------
-    if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         if (CheckCollisionPointRec(mousePoint, bounds))
         {
@@ -3925,14 +4049,14 @@
         // Draw vertical grid lines
         for (int i = 0; i < linesV; i++)
         {
-            Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height };
+            Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height + 1 };
             GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(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 };
+            Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width + 1, 1 };
             GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA));
         }
     }
@@ -3954,7 +4078,6 @@
 // Set tooltip string
 void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; }
 
-
 //----------------------------------------------------------------------------------
 // Styles loading functions
 //----------------------------------------------------------------------------------
@@ -3967,6 +4090,7 @@
     #define MAX_LINE_BUFFER_SIZE    256
 
     bool tryBinary = false;
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
 
     // Try reading the files as text file first
     FILE *rgsFile = fopen(fileName, "rt");
@@ -4600,7 +4724,7 @@
             }
         }
 
-        if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE - ICON_TEXT_PADDING);
+        if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
     }
 
     return (int)textSize.x;
@@ -4773,6 +4897,7 @@
         // Get text position depending on alignment and iconId
         //---------------------------------------------------------------------------------
         Vector2 textBoundsPosition = { textBounds.x, textBounds.y };
+        float textBoundsWidthOffset = 0.0f;
 
         // NOTE: We get text size after icon has been processed
         // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed?
@@ -4798,6 +4923,8 @@
             default: break;
         }
 
+        if (textSizeX > textBounds.width && (lines[i] != NULL) && (lines[i][0] != '\0')) textBoundsPosition.x = textBounds.x;
+
         switch (alignmentVertical)
         {
             // Only valid in case of wordWrap = 0;
@@ -4820,7 +4947,8 @@
         {
             // 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);
+            textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+            textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
         }
 #endif
         // Get size in bytes of text,
@@ -4829,9 +4957,15 @@
         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 lastSpaceIndex = 0;
+        bool tempWrapCharMode = false;
+
         int textOffsetY = 0;
         float textOffsetX = 0.0f;
         float glyphWidth = 0;
+
+        int ellipsisWidth = GetTextWidth("...");
+        bool textOverflow = false;
         for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
         {
             int codepoint = GetCodepointNext(&lines[i][c], &codepointSize);
@@ -4839,36 +4973,51 @@
 
             // 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
+            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
+            // 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;
+
+            // Wrap mode text measuring, to validate if
+            // it can be drawn or a new line is required
             if (wrapMode == TEXT_WRAP_CHAR)
             {
-                // 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;
-
                 // Jump to next line if current character reach end of the box limits
-                if ((textOffsetX + glyphWidth) > textBounds.width)
+                if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset)
                 {
                     textOffsetX = 0.0f;
                     textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+
+                    if (tempWrapCharMode)   // Wrap at char level when too long words
+                    {
+                        wrapMode = TEXT_WRAP_WORD;
+                        tempWrapCharMode = false;
+                    }
                 }
             }
             else if (wrapMode == TEXT_WRAP_WORD)
             {
+                if (codepoint == 32) lastSpaceIndex = c;
+
                 // Get width to next space in line
                 int nextSpaceIndex = 0;
                 float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex);
 
-                if ((textOffsetX + nextSpaceWidth) > textBounds.width)
+                int nextSpaceIndex2 = 0;
+                float nextWordSize = GetNextSpaceWidth(lines[i] + lastSpaceIndex + 1, &nextSpaceIndex2);
+
+                if (nextWordSize > textBounds.width - textBoundsWidthOffset)
                 {
+                    // Considering the case the next word is longer than bounds
+                    tempWrapCharMode = true;
+                    wrapMode = TEXT_WRAP_CHAR;
+                }
+                else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset)
+                {
                     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
@@ -4881,8 +5030,24 @@
                     if (wrapMode == TEXT_WRAP_NONE)
                     {
                         // Draw only required text glyphs fitting the textBounds.width
-                        if (textOffsetX <= (textBounds.width - glyphWidth))
+                        if (textSizeX > textBounds.width)
                         {
+                            if (textOffsetX <= (textBounds.width - glyphWidth - textBoundsWidthOffset - ellipsisWidth))
+                            {
+                                DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                            }
+                            else if (!textOverflow)
+                            {
+                                textOverflow = true;
+
+                                for (int j = 0; j < ellipsisWidth; j += ellipsisWidth/3)
+                                {
+                                    DrawTextCodepoint(guiFont, '.', RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX + j, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                                }
+                            }
+                        }
+                        else
+                        {
                             DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
                         }
                     }
@@ -4937,7 +5102,7 @@
 // Draw tooltip using control bounds
 static void GuiTooltip(Rectangle controlRec)
 {
-    if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiSliderDragging)
+    if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiControlExclusiveMode)
     {
         Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
 
@@ -5003,7 +5168,7 @@
             buffer[i] = '\0';   // Set an end of string at this point
 
             counter++;
-            if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+            if (counter > RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
         }
     }
 
@@ -5163,8 +5328,11 @@
     if (value > maxValue) value = maxValue;
     if (value < minValue) value = minValue;
 
-    const int valueRange = maxValue - minValue;
+    int valueRange = maxValue - minValue;
+    if (valueRange <= 0) valueRange = 1;
+
     int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+    if (sliderSize < 1) sliderSize = 1;  // TODO: Consider a minimum slider size
 
     // Calculate rectangles for all of the components
     arrowUpLeft = RAYGUI_CLITERAL(Rectangle){
@@ -5205,13 +5373,13 @@
     {
         Vector2 mousePoint = GetMousePosition();
 
-        if (guiSliderDragging) // Keep dragging outside of bounds
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
         {
             if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) &&
                 !CheckCollisionPointRec(mousePoint, arrowUpLeft) &&
                 !CheckCollisionPointRec(mousePoint, arrowDownRight))
             {
-                if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
                 {
                     state = STATE_PRESSED;
 
@@ -5221,8 +5389,8 @@
             }
             else
             {
-                guiSliderDragging = false;
-                guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
             }
         }
         else if (CheckCollisionPointRec(mousePoint, bounds))
@@ -5236,8 +5404,8 @@
             // Handle mouse button down
             if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
             {
-                guiSliderDragging = true;
-                guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
 
                 // Check arrows click
                 if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
@@ -5437,6 +5605,37 @@
     return value*sign;
 }
 
+// Get float value from text
+// NOTE: This function replaces atof() [stdlib.h]
+// WARNING: Only '.' character is understood as decimal point
+static float TextToFloat(const char *text)
+{
+    float value = 0.0f;
+    float sign = 1.0f;
+
+    if ((text[0] == '+') || (text[0] == '-'))
+    {
+        if (text[0] == '-') sign = -1.0f;
+        text++;
+    }
+
+    int i = 0;
+    for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
+
+    if (text[i++] != '.') value *= sign;
+    else
+    {
+        float divisor = 10.0f;
+        for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+        {
+            value += ((float)(text[i] - '0'))/divisor;
+            divisor = divisor*10.0f;
+        }
+    }
+
+    return value;
+}
+
 // Encode codepoint into UTF-8 text (char array size returned as parameter)
 static const char *CodepointToUTF8(int codepoint, int *byteSize)
 {
@@ -5490,21 +5689,21 @@
     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
+        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
+        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
+        if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks
         codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
         *codepointSize = 2;
     }
@@ -5514,7 +5713,6 @@
         codepoint = ptr[0];
         *codepointSize = 1;
     }
-
 
     return codepoint;
 }
diff --git a/raylib/examples/shapes/shapes_bouncing_ball.c b/raylib/examples/shapes/shapes_bouncing_ball.c
--- a/raylib/examples/shapes/shapes_bouncing_ball.c
+++ b/raylib/examples/shapes/shapes_bouncing_ball.c
@@ -62,7 +62,7 @@
             ClearBackground(RAYWHITE);
 
             DrawCircleV(ballPosition, (float)ballRadius, MAROON);
-            //DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY);
+            DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY);
 
             // On pause, we draw a blinking message
             if (pause && ((framesCounter/30)%2)) DrawText("PAUSED", 350, 200, 30, GRAY);
diff --git a/raylib/examples/shapes/shapes_draw_circle_sector.c b/raylib/examples/shapes/shapes_draw_circle_sector.c
--- a/raylib/examples/shapes/shapes_draw_circle_sector.c
+++ b/raylib/examples/shapes/shapes_draw_circle_sector.c
@@ -63,11 +63,11 @@
 
             // Draw GUI controls
             //------------------------------------------------------------------------------
-            GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", NULL, &startAngle, 0, 720);
-            GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", NULL, &endAngle, 0, 720);
+            GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, 0, 720);
+            GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, 0, 720);
 
-            GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", NULL, &outerRadius, 0, 200);
-            GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", NULL, &segments, 0, 100);
+            GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200);
+            GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 100);
             //------------------------------------------------------------------------------
 
             minSegments = truncf(ceilf((endAngle - startAngle) / 90));
diff --git a/raylib/examples/shapes/shapes_draw_rectangle_rounded.c b/raylib/examples/shapes/shapes_draw_rectangle_rounded.c
--- a/raylib/examples/shapes/shapes_draw_rectangle_rounded.c
+++ b/raylib/examples/shapes/shapes_draw_rectangle_rounded.c
@@ -66,11 +66,11 @@
 
             // Draw GUI controls
             //------------------------------------------------------------------------------
-            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);
+            GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", TextFormat("%.2f", width), &width, 0, (float)GetScreenWidth() - 300);
+            GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", TextFormat("%.2f", height), &height, 0, (float)GetScreenHeight() - 50);
+            GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", TextFormat("%.2f", roundness), &roundness, 0.0f, 1.0f);
+            GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", TextFormat("%.2f", lineThick), &lineThick, 0, 20);
+            GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 60);
 
             GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", &drawRoundedRect);
             GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", &drawRoundedLines);
diff --git a/raylib/examples/shapes/shapes_draw_ring.c b/raylib/examples/shapes/shapes_draw_ring.c
--- a/raylib/examples/shapes/shapes_draw_ring.c
+++ b/raylib/examples/shapes/shapes_draw_ring.c
@@ -69,13 +69,13 @@
 
             // Draw GUI controls
             //------------------------------------------------------------------------------
-            GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", NULL, &startAngle, -450, 450);
-            GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", NULL, &endAngle, -450, 450);
+            GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, -450, 450);
+            GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, -450, 450);
 
-            GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", NULL, &innerRadius, 0, 100);
-            GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", NULL, &outerRadius, 0, 200);
+            GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", TextFormat("%.2f", innerRadius), &innerRadius, 0, 100);
+            GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200);
 
-            GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", NULL, &segments, 0, 100);
+            GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", TextFormat("%.2f", segments), &segments, 0, 100);
 
             GuiCheckBox((Rectangle){ 600, 320, 20, 20 }, "Draw Ring", &drawRing);
             GuiCheckBox((Rectangle){ 600, 350, 20, 20 }, "Draw RingLines", &drawRingLines);
diff --git a/raylib/examples/text/text_input_box.c b/raylib/examples/text/text_input_box.c
--- a/raylib/examples/text/text_input_box.c
+++ b/raylib/examples/text/text_input_box.c
@@ -35,7 +35,7 @@
 
     int framesCounter = 0;
 
-    SetTargetFPS(10);               // Set our game to run at 10 frames-per-second
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
 
     // Main game loop
diff --git a/raylib/examples/text/text_writing_anim.c b/raylib/examples/text/text_writing_anim.c
--- a/raylib/examples/text/text_writing_anim.c
+++ b/raylib/examples/text/text_writing_anim.c
@@ -52,7 +52,7 @@
             DrawText(TextSubtext(message, 0, framesCounter/10), 210, 160, 20, MAROON);
 
             DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, LIGHTGRAY);
-            DrawText("PRESS [SPACE] to SPEED UP!", 239, 300, 20, LIGHTGRAY);
+            DrawText("HOLD [SPACE] to SPEED UP!", 239, 300, 20, LIGHTGRAY);
 
         EndDrawing();
         //----------------------------------------------------------------------------------
diff --git a/raylib/examples/textures/textures_blend_modes.c b/raylib/examples/textures/textures_blend_modes.c
--- a/raylib/examples/textures/textures_blend_modes.c
+++ b/raylib/examples/textures/textures_blend_modes.c
@@ -43,6 +43,9 @@
     const int blendCountMax = 4;
     BlendMode blendMode = 0;
 
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //---------------------------------------------------------------------------------------
+
     // Main game loop
     while (!WindowShouldClose())    // Detect window close button or ESC key
     {
diff --git a/raylib/examples/textures/textures_image_drawing.c b/raylib/examples/textures/textures_image_drawing.c
--- a/raylib/examples/textures/textures_image_drawing.c
+++ b/raylib/examples/textures/textures_image_drawing.c
@@ -47,7 +47,7 @@
 
     UnloadImage(cat);       // Unload image from RAM
 
-    // Load custom font for frawing on image
+    // Load custom font for drawing on image
     Font font = LoadFont("resources/custom_jupiter_crash.png");
 
     // Draw over image using custom font
diff --git a/raylib/examples/textures/textures_image_rotate.c b/raylib/examples/textures/textures_image_rotate.c
--- a/raylib/examples/textures/textures_image_rotate.c
+++ b/raylib/examples/textures/textures_image_rotate.c
@@ -43,6 +43,8 @@
     textures[2] = LoadTextureFromImage(imageNeg90);
 
     int currentTexture = 0;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //---------------------------------------------------------------------------------------
 
     // Main game loop
diff --git a/raylib/examples/textures/textures_logo_raylib.c b/raylib/examples/textures/textures_logo_raylib.c
--- a/raylib/examples/textures/textures_logo_raylib.c
+++ b/raylib/examples/textures/textures_logo_raylib.c
@@ -27,6 +27,8 @@
 
     // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
     Texture2D texture = LoadTexture("resources/raylib_logo.png");        // Texture loading
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //---------------------------------------------------------------------------------------
 
     // Main game loop
diff --git a/raylib/examples/textures/textures_raw_data.c b/raylib/examples/textures/textures_raw_data.c
--- a/raylib/examples/textures/textures_raw_data.c
+++ b/raylib/examples/textures/textures_raw_data.c
@@ -63,6 +63,8 @@
 
     Texture2D checked = LoadTextureFromImage(checkedIm);
     UnloadImage(checkedIm);         // Unload CPU (RAM) image data (pixels)
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //---------------------------------------------------------------------------------------
 
     // Main game loop
diff --git a/raylib/examples/textures/textures_sprite_explosion.c b/raylib/examples/textures/textures_sprite_explosion.c
--- a/raylib/examples/textures/textures_sprite_explosion.c
+++ b/raylib/examples/textures/textures_sprite_explosion.c
@@ -48,8 +48,8 @@
     bool active = false;
     int framesCounter = 0;
 
-    SetTargetFPS(120);
-    //--------------------------------------------------------------------------------------
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //---------------------------------------------------------------------------------------
 
     // Main game loop
     while (!WindowShouldClose())    // Detect window close button or ESC key
diff --git a/raylib/examples/textures/textures_svg_loading.c b/raylib/examples/textures/textures_svg_loading.c
deleted file mode 100644
--- a/raylib/examples/textures/textures_svg_loading.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*******************************************************************************************
-*
-*   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;
-}
diff --git a/raylib/examples/textures/textures_to_image.c b/raylib/examples/textures/textures_to_image.c
--- a/raylib/examples/textures/textures_to_image.c
+++ b/raylib/examples/textures/textures_to_image.c
@@ -38,6 +38,8 @@
 
     texture = LoadTextureFromImage(image);                 // Recreate texture from retrieved image data (RAM -> VRAM)
     UnloadImage(image);                                    // Unload retrieved image data from CPU memory (RAM)
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //---------------------------------------------------------------------------------------
 
     // Main game loop
diff --git a/raylib/parser/raylib_parser.c b/raylib/parser/raylib_parser.c
--- a/raylib/parser/raylib_parser.c
+++ b/raylib/parser/raylib_parser.c
@@ -72,7 +72,7 @@
 #define MAX_CALLBACKS_TO_PARSE    64    // Maximum number of callbacks to parse
 #define MAX_FUNCS_TO_PARSE      1024    // Maximum number of functions to parse
 
-#define MAX_LINE_LENGTH          512    // Maximum length of one line (including comments)
+#define MAX_LINE_LENGTH         1024    // Maximum length of one line (including comments)
 
 #define MAX_STRUCT_FIELDS         64    // Maximum number of struct fields
 #define MAX_ENUM_VALUES          512    // Maximum number of enum values
@@ -139,7 +139,7 @@
 // Function info data
 typedef struct FunctionInfo {
     char name[64];              // Function name
-    char desc[128];             // Function description (comment at the end)
+    char desc[512];             // Function description (comment at the end)
     char retType[32];           // Return value type
     int paramCount;             // Number of function parameters
     char paramType[MAX_FUNCTION_PARAMETERS][32];   // Parameters type
diff --git a/raylib/src/config.h b/raylib/src/config.h
--- a/raylib/src/config.h
+++ b/raylib/src/config.h
@@ -113,13 +113,23 @@
 #define RL_CULL_DISTANCE_FAR              1000.0      // Default projection matrix far cull distance
 
 // Default shader vertex attribute locations
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION  0
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD  1
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL    2
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR     3
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT   4
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION    0
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD    1
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL      2
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR       3
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT     4
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2   5
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES     6
 
+
+#define RL_SUPPORT_MESH_GPU_SKINNING                   // Remove this if your GPU does not support more than 8 VBOs
+
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS     7
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
+#endif
+
+
 // Default shader vertex attribute names to set location points
 // NOTE: When a new shader is loaded, the following locations are tried to be set for convenience
 #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION     "vertexPosition"    // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
@@ -170,7 +180,6 @@
 //#define SUPPORT_FILEFORMAT_ASTC     1
 //#define SUPPORT_FILEFORMAT_PKM      1
 //#define SUPPORT_FILEFORMAT_PVR      1
-//#define SUPPORT_FILEFORMAT_SVG      1
 
 // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi)
 #define SUPPORT_IMAGE_EXPORT            1
@@ -225,7 +234,12 @@
 // rmodels: Configuration values
 //------------------------------------------------------------------------------------
 #define MAX_MATERIAL_MAPS              12       // Maximum number of shader maps supported
+
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+#define MAX_MESH_VERTEX_BUFFERS         9       // Maximum vertex buffers (VBO) per mesh
+#else
 #define MAX_MESH_VERTEX_BUFFERS         7       // Maximum vertex buffers (VBO) per mesh
+#endif
 
 //------------------------------------------------------------------------------------
 // Module: raudio - Configuration Flags
diff --git a/raylib/src/external/RGFW.h b/raylib/src/external/RGFW.h
--- a/raylib/src/external/RGFW.h
+++ b/raylib/src/external/RGFW.h
@@ -54,6580 +54,8940 @@
 	#define RGFW_MALLOC x - choose what function to use to allocate, by default the standard malloc is used
 	#define RGFW_CALLOC x - choose what function to use to allocate (calloc), by default the standard calloc is used
 	#define RGFW_FREE x - choose what function to use to allocated memory, by default the standard free is used
-*/
-
-/*
-	Credits :
-		EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support, siliapp.h -> referencing 
-
-		stb - This project is heavily inspired by the stb single header files
-
-		GLFW:
-			certain parts of winapi and X11 are very poorly documented,
-			GLFW's source code was referenced and used throughout the project (used code is marked in some way),
-			this mainly includes, code for drag and drops, code for setting the icon to a bitmap and the code for managing the clipboard for X11 (as these parts are not documented very well)
-
-			GLFW Copyright, https::/github.com/GLFW/GLFW
-
-			Copyright (c) 2002-2006 Marcus Geelnard
-			Copyright (c) 2006-2019 Camilla Löwy
-
-		contributors : (feel free to put yourself here if you contribute)
-		krisvers -> code review
-		EimaMei (SaCode) -> code review
-		Code-Nycticebus -> bug fixes
-		Rob Rohan -> X11 bugs and missing features
-		AICDG (@THISISAGOODNAME) -> vulkan support (example)
-*/
-
-#ifndef RGFW_MALLOC
-#include <stdlib.h>
-#include <time.h>
-#define RGFW_MALLOC malloc
-#define RGFW_CALLOC calloc
-#define RGFW_FREE free
-#endif
-
-#if !_MSC_VER
-#ifndef inline
-#ifndef __APPLE__
-#define inline __inline
-#endif
-#endif
-#endif
-
-/* for windows 95 testing (not that it works well) */
-#ifdef RGFW_WIN95
-#define RGFW_NO_MONITOR
-#define RGFW_NO_PASSTHROUGH
-#endif
-
-#ifndef RGFWDEF
-#ifdef __APPLE__
-#define RGFWDEF static inline
-#else
-#define RGFWDEF inline
-#endif
-#endif
-
-#ifndef RGFW_ENUM
-#define RGFW_ENUM(type, name) type name; enum
-#endif
-
-#ifndef RGFW_UNUSED
-#define RGFW_UNUSED(x) (void)(x);
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-	/* makes sure the header file part is only defined once by default */
-#ifndef RGFW_HEADER
-
-#define RGFW_HEADER
-
-#if !defined(u8)
-	#if defined(_MSC_VER) || defined(__SYMBIAN32__)
-		typedef unsigned char 	u8;
-		typedef signed char		i8;
-		typedef unsigned short  u16;
-		typedef signed short 	i16;
-		typedef unsigned int 	u32;
-		typedef signed int		i32;
-		typedef unsigned long	u64;
-		typedef signed long		i64;
-	#else
-		#include <stdint.h>
-
-		typedef uint8_t     u8;
-		typedef int8_t      i8;
-		typedef uint16_t   u16;
-		typedef int16_t    i16;
-		typedef uint32_t   u32;
-		typedef int32_t    i32;
-		typedef uint64_t   u64;
-		typedef int64_t    i64;
-	#endif
-#endif
-
-#if !defined(b8)
-	typedef u8 b8;
-#endif
-
-#if defined(RGFW_X11) && defined(__APPLE__)
-#define RGFW_MACOS_X11
-#undef __APPLE__
-#endif
-
-#if defined(_WIN32) && !defined(RGFW_X11) /* (if you're using X11 on windows some how) */
-
-	/* this name looks better */
-	/* plus it helps with cross-compiling because RGFW_X11 won't be accidently defined */
-	
-#define RGFW_WINDOWS
-
-#if defined(_WIN32) && !defined(WIN32)
-#define WIN32
-#endif
-
-#if defined(_WIN64)
-
-#ifndef WIN64
-#define WIN64
-#endif
-
-#define _AMD64_
-#undef _X86_
-#else
-#undef _AMD64_
-#ifndef _X86_
-#define _X86_
-#endif
-#endif
-
-#ifndef RGFW_NO_XINPUT
-#ifdef __MINGW32__
-#include <xinput.h>
-#else
-#include <XInput.h>
-#endif
-#endif
-
-#else 
-#if defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)
-#define RGFW_MACOS_X11
-#define RGFW_X11
-#include <X11/Xlib.h>
-#endif
-#endif 	
-
-#if defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11)
-#define RGFW_MACOS
-#endif
-
-#if (defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)) && !defined(RGFW_EGL)
-#define RGFW_EGL
-#endif
-#if defined(RGFW_EGL) && defined(__APPLE__)
-	#warning  EGL is not supported for Cocoa, switching back to the native opengl api
-#undef RGFW_EGL
-#endif
-
-#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API)
-#define RGFW_OPENGL
-#endif
-
-#if defined(RGFW_X11) && (defined(RGFW_OPENGL))
-#ifndef GLX_MESA_swap_control
-#define  GLX_MESA_swap_control
-#endif
-#include <GL/glx.h> /* GLX defs, xlib.h, gl.h */
-#endif
-
-#ifdef RGFW_EGL
-#include <EGL/egl.h>
-#endif
-
-#ifdef RGFW_OSMESA
-#ifndef __APPLE__
-#include <GL/osmesa.h>
-#else
-#include <OpenGL/osmesa.h>
-#endif
-#endif
-
-#if defined(RGFW_DIRECTX) && defined(RGFW_WINDOWS)
-#include <d3d11.h>
-#include <dxgi.h>
-#include <dxgi.h>
-#include <d3dcompiler.h>
-
-#ifndef __cplusplus
-#define __uuidof(T) IID_##T
-#endif
-#endif
-
-#ifndef RGFW_ALPHA
-#define RGFW_ALPHA 128 /* alpha value for RGFW_TRANSPARENT_WINDOW (WINAPI ONLY, macOS + linux don't need this) */
-#endif
-
-/*! Optional arguments for making a windows */
-#define RGFW_TRANSPARENT_WINDOW		(1L<<9) /*!< the window is transparent (only properly works on X11 and MacOS, although it's although for windows) */
-#define RGFW_NO_BORDER		(1L<<3) /*!< the window doesn't have border */
-#define RGFW_NO_RESIZE		(1L<<4) /*!< the window cannot be resized  by the user */
-#define RGFW_ALLOW_DND     (1L<<5) /*!< the window supports drag and drop*/
-#define RGFW_HIDE_MOUSE (1L<<6) /*! the window should hide the mouse or not (can be toggled later on) using `RGFW_window_mouseShow*/
-#define RGFW_FULLSCREEN (1L<<8) /* the window is fullscreen by default or not */
-#define RGFW_CENTER (1L<<10) /*! center the window on the screen */
-#define RGFW_OPENGL_SOFTWARE (1L<<11) /*! use OpenGL software rendering */
-#define RGFW_COCOA_MOVE_TO_RESOURCE_DIR (1L << 12) /* (cocoa only), move to resource folder */
-#define RGFW_SCALE_TO_MONITOR (1L << 13) /* scale the window to the screen */
-#define RGFW_NO_INIT_API (1L << 2) /* DO not init an API (mostly for bindings, you should use `#define RGFW_NO_API` in C */
-
-#define RGFW_NO_GPU_RENDER (1L<<14) /* don't render (using the GPU based API)*/
-#define RGFW_NO_CPU_RENDER (1L<<15) /* don't render (using the CPU based buffer rendering)*/
-
-
-/*! event codes */
-#define RGFW_keyPressed 2 /* a key has been pressed */
-#define RGFW_keyReleased 3 /*!< a key has been released*/
-/*! key event note
-	the code of the key pressed is stored in
-	RGFW_Event.keyCode
-	!!Keycodes defined at the bottom of the RGFW_HEADER part of this file!!
-
-	while a string version is stored in
-	RGFW_Event.KeyString
-
-	RGFW_Event.lockState holds the current lockState
-	this means if CapsLock, NumLock are active or not
-*/
-#define RGFW_mouseButtonPressed 4 /*!< a mouse button has been pressed (left,middle,right)*/
-#define RGFW_mouseButtonReleased 5 /*!< a mouse button has been released (left,middle,right)*/
-#define RGFW_mousePosChanged 6 /*!< the position of the mouse has been changed*/
-/*! mouse event note
-	the x and y of the mouse can be found in the vector, RGFW_Event.point
-
-	RGFW_Event.button holds which mouse button was pressed
-*/
-#define RGFW_jsButtonPressed 7 /*!< a joystick button was pressed */
-#define RGFW_jsButtonReleased 8 /*!< a joystick button was released */
-#define RGFW_jsAxisMove 9 /*!< an axis of a joystick was moved*/
-/*! joystick event note
-	RGFW_Event.joystick holds which joystick was altered, if any
-	RGFW_Event.button holds which joystick button was pressed
-
-	RGFW_Event.axis holds the data of all the axis
-	RGFW_Event.axisCount says how many axis there are
-*/
-#define RGFW_windowMoved 10 /*!< the window was moved (by the user) */
-#define RGFW_windowResized 11 /*!< the window was resized (by the user) */
-
-#define RGFW_focusIn 12 /*!< window is in focus now */
-#define RGFW_focusOut 13 /*!< window is out of focus now */
-
-#define RGFW_mouseEnter 14 /* mouse entered the window */
-#define RGFW_mouseLeave 15 /* mouse left the window */
-
-#define RGFW_windowRefresh 16 /* The window content needs to be refreshed */
-
-/* attribs change event note
-	The event data is sent straight to the window structure
-	with win->r.x, win->r.y, win->r.w and win->r.h
-*/
-#define RGFW_quit 33 /*!< the user clicked the quit button*/ 
-#define RGFW_dnd 34 /*!< a file has been dropped into the window*/
-#define RGFW_dnd_init 35 /*!< the start of a dnd event, when the place where the file drop is known */
-/* dnd data note
-	The x and y coords of the drop are stored in the vector RGFW_Event.point
-
-	RGFW_Event.droppedFilesCount holds how many files were dropped
-
-	This is also the size of the array which stores all the dropped file string,
-	RGFW_Event.droppedFiles
-*/
-
-/*! mouse button codes (RGFW_Event.button) */
-#define RGFW_mouseLeft  1 /*!< left mouse button is pressed*/
-#define RGFW_mouseMiddle  2 /*!< mouse-wheel-button is pressed*/
-#define RGFW_mouseRight  3 /*!< right mouse button is pressed*/
-#define RGFW_mouseScrollUp  4 /*!< mouse wheel is scrolling up*/
-#define RGFW_mouseScrollDown  5 /*!< mouse wheel is scrolling down*/
-
-#ifndef RGFW_MAX_PATH
-#define RGFW_MAX_PATH 260 /* max length of a path (for dnd) */
-#endif
-#ifndef RGFW_MAX_DROPS
-#define RGFW_MAX_DROPS 260 /* max items you can drop at once */
-#endif
-
-
-/* for RGFW_Event.lockstate */
-#define RGFW_CAPSLOCK (1L << 1)
-#define RGFW_NUMLOCK (1L << 2)
-
-/*! joystick button codes (based on xbox/playstation), you may need to change these values per controller */
-#ifndef RGFW_joystick_codes
-
-typedef RGFW_ENUM(u8, RGFW_joystick_codes) {
-	RGFW_JS_A = 0, /* or PS X button */
-	RGFW_JS_B = 1, /* or PS circle button */
-	RGFW_JS_Y = 2, /* or PS triangle button */
-	RGFW_JS_X = 3, /* or PS square button */
-	RGFW_JS_START = 9, /* start button */
-	RGFW_JS_SELECT = 8, /* select button */
-	RGFW_JS_HOME = 10, /* home button */
-	RGFW_JS_UP = 13, /* dpad up */
-	RGFW_JS_DOWN = 14, /* dpad down*/
-	RGFW_JS_LEFT = 15, /* dpad left */
-	RGFW_JS_RIGHT = 16, /* dpad right */
-	RGFW_JS_L1 = 4, /* left bump */
-	RGFW_JS_L2 = 5, /* left trigger*/
- 	RGFW_JS_R1 = 6, /* right bumper */
-	RGFW_JS_R2 = 7, /* right trigger */
-};
-
-#endif
-
-/* basic vector type, if there's not already a point/vector type of choice */
-#ifndef RGFW_vector
-typedef struct { i32 x, y; } RGFW_vector;
-#endif
-
-	/* basic rect type, if there's not already a rect type of choice */
-#ifndef RGFW_rect
-	typedef struct { i32 x, y, w, h; } RGFW_rect;
-#endif
-
-	/* basic area type, if there's not already a area type of choice */
-#ifndef RGFW_area
-	typedef struct { u32 w, h; } RGFW_area;
-#endif
-
-#define RGFW_VECTOR(x, y) (RGFW_vector){x, y}
-#define RGFW_RECT(x, y, w, h) (RGFW_rect){x, y, w, h}
-#define RGFW_AREA(w, h) (RGFW_area){w, h}
-
-	#ifndef RGFW_NO_MONITOR
-	typedef struct RGFW_monitor {
-		char name[128];  /* monitor name */
-		RGFW_rect rect; /* monitor Workarea */
-		float scaleX, scaleY; /* monitor content scale*/
-		float physW, physH; /* monitor physical size */
-	} RGFW_monitor;
-
-	/*
-	NOTE : Monitor functions should be ran only as many times as needed (not in a loop)
-	*/
-
-	/* get an array of all the monitors (max 6) */
-	RGFWDEF RGFW_monitor* RGFW_getMonitors(void);
-	/* get the primary monitor */
-	RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void);
-	#endif
-
-	/* NOTE: some parts of the data can represent different things based on the event (read comments in RGFW_Event struct) */
-	typedef struct RGFW_Event {
-		char keyName[16]; /* key name of event*/
-
-		/*! drag and drop data */
-		/* 260 max paths with a max length of 260 */
-#ifdef RGFW_ALLOC_DROPFILES
-		char** droppedFiles;
-#else
-		char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH]; /*!< dropped files*/
-#endif
-		u32 droppedFilesCount; /*!< house many files were dropped */
-
-		u32 type; /*!< which event has been sent?*/
-		RGFW_vector point; /*!< mouse x, y of event (or drop point) */
-		
-		u32 fps; /*the current fps of the window [the fps is checked when events are checked]*/
-		u64 frameTime, frameTime2; /* this is used for counting the fps */
-		
-		u8 keyCode; /*!< keycode of event 	!!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */
-
-		b8 inFocus;  /*if the window is in focus or not (this is always true for MacOS windows due to the api being weird) */
-
-		u8 lockState;
-
-		u16 joystick; /* which joystick this event applies to (if applicable to any) */
-
-		u8 button; /*!< which mouse button has been clicked (0) left (1) middle (2) right OR which joystick button was pressed*/
-		double scroll; /* the raw mouse scroll value */
-
-		u8 axisesCount; /* number of axises */
-		RGFW_vector axis[2]; /* x, y of axises (-100 to 100) */
-	} RGFW_Event; /*!< Event structure for checking/getting events */
-
-	/* source data for the window (used by the APIs) */
-	typedef struct RGFW_window_src {
-#ifdef RGFW_WINDOWS
-		HWND window; /*!< source window */
-		HDC hdc; /*!< source HDC */
-		u32 hOffset; /*!< height offset for window */
-#endif
-#ifdef RGFW_X11
-		Display* display; /*!< source display */
-		Window window; /*!< source window */
-#endif
-#ifdef RGFW_MACOS
-		u32 display;
-		void* displayLink;
-		void* window;
-		b8 dndPassed;
-#endif
-
-#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA)
-#ifdef RGFW_MACOS
-		void* rSurf; /*!< source graphics context */
-#endif
-#ifdef RGFW_WINDOWS
-		HGLRC rSurf; /*!< source graphics context */
-#endif
-#ifdef RGFW_X11
-		GLXContext rSurf; /*!< source graphics context */
-#endif
-#else
-
-#ifdef RGFW_OSMESA
-		OSMesaContext rSurf;
-#endif
-#endif
-
-#ifdef RGFW_WINDOWS
-		RGFW_area maxSize, minSize;
-#if defined(RGFW_DIRECTX)
-		IDXGISwapChain* swapchain;
-		ID3D11RenderTargetView* renderTargetView;
-		ID3D11DepthStencilView* pDepthStencilView;
-#endif
-#endif
-
-#if defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
-		void* view; /*apple viewpoint thingy*/
-#endif
-
-#ifdef RGFW_EGL
-		EGLSurface EGL_surface;
-		EGLDisplay EGL_display;
-		EGLContext EGL_context;
-#endif
-
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
-#ifdef RGFW_WINDOWS
-		HBITMAP bitmap;
-#endif
-#ifdef RGFW_X11
-		XImage* bitmap;
-		GC gc;
-#endif
-#ifdef RGFW_MACOS
-		void* bitmap; /* API's bitmap for storing or managing */
-		void* image;
-#endif
-#if defined(RGFW_BUFFER) && defined(RGFW_WINDOWS)
-		HDC hdcMem; /* window stored in memory that winapi needs to render buffers */
-#endif
-#endif
-
-		u8 jsPressed[4][16]; /* if a key is currently pressed or not (per joystick) */
-
-		i32 joysticks[4]; /* limit of 4 joysticks at a time */
-		u16 joystickCount; /* the actual amount of joysticks */
-
-		RGFW_area scale; /* window scaling */
-
-#ifdef RGFW_MACOS
-		b8 cursorChanged; /* for steve jobs */
-#endif
-
-		u32 winArgs; /* windows args (for RGFW to check) */
-		/*
-			!< if dnd is enabled or on (based on window creating args)
-			cursorChanged
-		*/
-	} RGFW_window_src;
-
-	typedef struct RGFW_window {
-		RGFW_window_src src;
-
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
-		u8* buffer; /* buffer for non-GPU systems (OSMesa, basic software rendering) */
-		/* when rendering using RGFW_BUFFER, the buffer is in the RGBA format */
-#endif
-
-		RGFW_Event event; /*!< current event */
-
-		RGFW_rect r; /* the x, y, w and h of the struct */
-
-		u32 fpsCap; /*!< the fps cap of the window should run at (change this var to change the fps cap, 0 = no limit)*/
-		/*[the fps is capped when events are checked]*/
-	} RGFW_window; /*!< Window structure for managing the window */
-
-#if defined(RGFW_X11) || defined(RGFW_MACOS)
-	typedef u64 RGFW_thread; /* thread type unix */
-#else
-	typedef void* RGFW_thread; /* thread type for window */
-#endif
-
-	/* this has to be set before createWindow is called, else the fulscreen size is used */
-	RGFWDEF void RGFW_setBufferSize(RGFW_area size); /* the buffer cannot be resized (by RGFW) */
-
-	RGFW_window* RGFW_createWindow(
-		const char* name, /* name of the window */
-		RGFW_rect rect, /* rect of window */
-		u16 args /* extra arguments (NULL / (u16)0 means no args used)*/
-	); /*!< function to create a window struct */
-
-	/* get the size of the screen to an area struct */
-	RGFWDEF RGFW_area RGFW_getScreenSize(void);
-
-	/*
-		this function checks an *individual* event (and updates window structure attributes)
-		this means, using this function without a while loop may cause event lag
-
-		ex.
-
-		while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one]
-
-		this function is optional if you choose to use event callbacks, 
-		although you still need some way to tell RGFW to process events eg. `RGFW_window_checkEvents`
-	*/
-
-	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win); /*!< check current event (returns a pointer to win->event or NULL if there is no event)*/
-
-	/* 
-		check all the events until there are none left,  
-		this should only be used if you're using callbacks only
-	*/
-	RGFWDEF void RGFW_window_checkEvents(RGFW_window* win);
-
-
-	/*! window managment functions*/
-	RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */
-
-	RGFWDEF void RGFW_window_move(RGFW_window* win,
-		RGFW_vector v/* new pos*/
-	);
-
-	#ifndef RGFW_NO_MONITOR
-	/* move to a specific monitor */
-	RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m);
-	#endif
-	RGFWDEF void RGFW_window_resize(RGFW_window* win,
-		RGFW_area a/* new size*/
-	);
-
-	/* set the minimum size a user can shrink a window */
-	RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a);
-	/* set the minimum size a user can extend a window */
-	RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a);
-
-	RGFWDEF void RGFW_window_maximize(RGFW_window* win); /* maximize the window size */
-	RGFWDEF void RGFW_window_minimize(RGFW_window* win); /* minimize the window (in taskbar (per OS))*/
-	RGFWDEF void RGFW_window_restore(RGFW_window* win); /* restore the window from minimized (per OS)*/
-
-	RGFWDEF void RGFW_window_setBorder(RGFW_window* win, b8 border); /* if the window should have a border or not (borderless) based on bool value of `border` */
-	
-	RGFWDEF void RGFW_window_setDND(RGFW_window* win, b8 allow); /* turn on / off dnd (RGFW_ALLOW_DND stil must be passed to the window)*/
-
-	#ifndef RGFW_NO_PASSTHROUGH
-	RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough); /* turn on / off mouse passthrough */
-	#endif 
-
-	RGFWDEF void RGFW_window_setName(RGFW_window* win,
-		char* name
-	);
-
-	void RGFW_window_setIcon(RGFW_window* win, /*!< source window */
-		u8* icon /*!< icon bitmap */,
-		RGFW_area a /*!< width and height of the bitmap*/,
-		i32 channels /*!< how many channels the bitmap has (rgb : 3, rgba : 4) */
-	); /*!< image resized by default */
-
-	/*!< sets mouse to bitmap (very simular to RGFW_window_setIcon), image NOT resized by default*/
-	RGFWDEF void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels);
-
-	/*!< sets the mouse to a standard API cursor (based on RGFW_MOUSE, as seen at the end of the RGFW_HEADER part of this file) */
-	RGFWDEF	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse);
-
-	RGFWDEF void RGFW_window_setMouseDefault(RGFW_window* win); /* sets the mouse to1` the default mouse image */
-	/*
-		holds the mouse in place by moving the mouse back each time it moves
-		you can still use win->event.point to see how much it moved before it was put back in place
-
-		this is useful for a 3D camera
-	*/
-	RGFWDEF void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area);
-	/* undo hold */
-	RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win);
-
-	/* hide the window */
-	RGFWDEF void RGFW_window_hide(RGFW_window* win);
-	/* show the window */
-	RGFWDEF void RGFW_window_show(RGFW_window* win);
-
-	/*
-		makes it so `RGFW_window_shouldClose` returns true
-		by setting the window event.type to RGFW_quit
-	*/
-	RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win);
-
-	/* where the mouse is on the screen */
-	RGFWDEF RGFW_vector RGFW_getGlobalMousePoint(void);
-
-	/* where the mouse is on the window */
-	RGFWDEF RGFW_vector RGFW_window_getMousePoint(RGFW_window* win);
-
-	/* show the mouse or hide the mouse*/
-	RGFWDEF void RGFW_window_showMouse(RGFW_window* win, i8 show);
-	/* move the mouse to a set x, y pos*/
-	RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector v);
-
-	/* if the window should close (RGFW_close was sent or escape was pressed) */
-	RGFWDEF b8 RGFW_window_shouldClose(RGFW_window* win);
-	/* if window is fullscreen'd */
-	RGFWDEF b8 RGFW_window_isFullscreen(RGFW_window* win);
-	/* if window is hidden */
-	RGFWDEF b8 RGFW_window_isHidden(RGFW_window* win);
-	/* if window is minimized */
-	RGFWDEF b8 RGFW_window_isMinimized(RGFW_window* win);
-	/* if window is maximized */
-	RGFWDEF b8 RGFW_window_isMaximized(RGFW_window* win);
-
-
-	#ifndef RGFW_NO_MONITOR
-	/*
-	scale the window to the monitor,
-	this is run by default if the user uses the arg `RGFW_SCALE_TO_MONITOR` during window creation
-	*/
-	RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win);
-	/* get the struct of the window's monitor  */
-	RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win);
-	#endif
-
-	/*!< make the window the current opengl drawing context */
-	RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win);
-
-	/*error handling*/
-	RGFWDEF b8 RGFW_Error(void); /* returns true if an error has occurred (doesn't print errors itself) */
-
-	/*!< if window == NULL, it checks if the key is pressed globally. Otherwise, it checks only if the key is pressed while the window in focus.*/
-	RGFWDEF b8 RGFW_isPressed(RGFW_window* win, u8 key); /*!< if key is pressed (key code)*/
-
-	RGFWDEF b8 RGFW_wasPressed(RGFW_window* win, u8 key); /*!< if key was pressed (checks prev keymap only) (key code)*/
-
-	RGFWDEF b8 RGFW_isHeld(RGFW_window* win, u8 key); /*!< if key is held (key code)*/
-	RGFWDEF b8 RGFW_isReleased(RGFW_window* win, u8 key); /*!< if key is released (key code)*/
-
-	RGFWDEF b8 RGFW_isClicked(RGFW_window* win, u8 key);
-
-	RGFWDEF b8 RGFW_isMousePressed(RGFW_window* win, u8 button);
-	RGFWDEF b8 RGFW_isMouseHeld(RGFW_window* win, u8 button);
-	RGFWDEF b8 RGFW_isMouseReleased(RGFW_window* win, u8 button);
-	RGFWDEF b8 RGFW_wasMousePressed(RGFW_window* win, u8 button);
-
-/*! clipboard functions*/
-	RGFWDEF char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */
-	RGFWDEF void RGFW_clipboardFree(char* str); /* the string returned from RGFW_readClipboard must be freed */
-
-	RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */
-
-	/* 
-		
-		
-		Event callbacks, 
-		these are completely optional, you can use the normal 
-		RGFW_checkEvent() method if you prefer that
-
-	*/
-
-	/* RGFW_windowMoved, the window and its new rect value  */
-	typedef void (* RGFW_windowmovefunc)(RGFW_window* win, RGFW_rect r);
-	/* RGFW_windowResized, the window and its new rect value  */
-	typedef void (* RGFW_windowresizefunc)(RGFW_window* win, RGFW_rect r);
-	/* RGFW_quit, the window that was closed */
-	typedef void (* RGFW_windowquitfunc)(RGFW_window* win);
-	/* RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its inFocus */
-	typedef void (* RGFW_focusfunc)(RGFW_window* win, b8 inFocus);
-	/* RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */
-	typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, RGFW_vector point, b8 status);
-	/* RGFW_mousePosChanged, the window that the move happened on and the new point of the mouse  */
-	typedef void (* RGFW_mouseposfunc)(RGFW_window* win, RGFW_vector point);
-	/* RGFW_dnd_init, the window, the point of the drop on the windows */
-	typedef void (* RGFW_dndInitfunc)(RGFW_window* win, RGFW_vector point);
-	/* RGFW_windowRefresh, the window that needs to be refreshed */
-	typedef void (* RGFW_windowrefreshfunc)(RGFW_window* win);
-	/* RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the keycode, the string version, the state of mod keys, if it was a press (else it's a release) */
-	typedef void (* RGFW_keyfunc)(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed);
-	/* RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release)  */
-	typedef void (* RGFW_mousebuttonfunc)(RGFW_window* win, u8 button, double scroll, b8 pressed);
-	/* RGFW_jsButtonPressed / RGFW_jsButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */
-	typedef void (* RGFW_jsButtonfunc)(RGFW_window* win, u16 joystick, u8 button, b8 pressed);
-	/* RGFW_jsAxisMove, the window that got the event, the joystick in question, the axis values and the amount of axises */
-	typedef void (* RGFW_jsAxisfunc)(RGFW_window* win, u16 joystick, RGFW_vector axis[2], u8 axisesCount);
-	
-	/*  RGFW_dnd, the window that had the drop, the drop data and the amount files dropped */
-	#ifdef RGFW_ALLOC_DROPFILES
-	typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount);
-	#else
-	typedef void (* RGFW_dndfunc)(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount);
-	#endif
-
-	RGFWDEF void RGFW_setWindowMoveCallback(RGFW_windowmovefunc func);
-	RGFWDEF void RGFW_setWindowResizeCallback(RGFW_windowresizefunc func);
-	RGFWDEF void RGFW_setWindowQuitCallback(RGFW_windowquitfunc func);
-	RGFWDEF void RGFW_setMousePosCallback(RGFW_mouseposfunc func);
-	RGFWDEF void RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func);
-	RGFWDEF void RGFW_setFocusCallback(RGFW_focusfunc func);
-	RGFWDEF void RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func);
-	RGFWDEF void RGFW_setDndCallback(RGFW_dndfunc func);
-	RGFWDEF void RGFW_setDndInitCallback(RGFW_dndInitfunc func);
-	RGFWDEF void RGFW_setKeyCallback(RGFW_keyfunc func);
-	RGFWDEF void RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func);
-	RGFWDEF void RGFW_setjsButtonCallback(RGFW_jsButtonfunc func);
-	RGFWDEF void RGFW_setjsAxisCallback(RGFW_jsAxisfunc func);
-
-
-#ifndef RGFW_NO_THREADS
-	/*! threading functions*/
-
-	/*! NOTE! (for X11/linux) : if you define a window in a thread, it must be run after the original thread's window is created or else there will be a memory error */
-	/*
-		I'd suggest you use sili's threading functions instead
-		if you're going to use sili
-		which is a good idea generally
-	*/
-
-	#if defined(__unix__) || defined(__APPLE__) 
-	typedef void* (* RGFW_threadFunc_ptr)(void*);
-	#else
-	typedef DWORD (__stdcall *RGFW_threadFunc_ptr) (LPVOID lpThreadParameter);  
-	#endif
-
-	RGFWDEF RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args); /*!< create a thread*/
-	RGFWDEF void RGFW_cancelThread(RGFW_thread thread); /*!< cancels a thread*/
-	RGFWDEF void RGFW_joinThread(RGFW_thread thread); /*!< join thread to current thread */
-	RGFWDEF void RGFW_setThreadPriority(RGFW_thread thread, u8 priority); /*!< sets the priority priority  */
-#endif
-
-	/*! gamepad/joystick functions (linux-only currently) */
-
-	/*! joystick count starts at 0*/
-	/*!< register joystick to window based on a number (the number is based on when it was connected eg. /dev/js0)*/
-	RGFWDEF u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber);
-	RGFWDEF u16 RGFW_registerJoystickF(RGFW_window* win, char* file);
-
-	RGFWDEF u32 RGFW_isPressedJS(RGFW_window* win, u16 controller, u8 button);
-
-	/*! native opengl functions */
-#ifdef RGFW_OPENGL
-/*! Get max OpenGL version */
-	RGFWDEF u8* RGFW_getMaxGLVersion(void);
-	/* OpenGL init hints */
-	RGFWDEF void RGFW_setGLStencil(i32 stencil); /* set stencil buffer bit size (8 by default) */
-	RGFWDEF void RGFW_setGLSamples(i32 samples); /* set number of sampiling buffers (4 by default) */
-	RGFWDEF void RGFW_setGLStereo(i32 stereo); /* use GL_STEREO (GL_FALSE by default) */
-	RGFWDEF void RGFW_setGLAuxBuffers(i32 auxBuffers); /* number of aux buffers (0 by default) */
-
-	/*! Set OpenGL version hint */
-	RGFWDEF void RGFW_setGLVersion(i32 major, i32 minor);
-	RGFWDEF void* RGFW_getProcAddress(const char* procname); /* get native opengl proc address */
-	RGFWDEF void RGFW_window_makeCurrent_OpenGL(RGFW_window* win); /* to be called by RGFW_window_makeCurrent */
-#endif
-	/* supports openGL, directX, OSMesa, EGL and software rendering */
-	RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /* swap the rendering buffer */
-	RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval);
-
-	RGFWDEF void RGFW_window_setGPURender(RGFW_window* win, i8 set);
-	RGFWDEF void RGFW_window_setCPURender(RGFW_window* win, i8 set);
-#ifdef RGFW_DIRECTX
-	typedef struct {
-		IDXGIFactory* pFactory;
-		IDXGIAdapter* pAdapter;
-		ID3D11Device* pDevice;
-		ID3D11DeviceContext* pDeviceContext;
-	} RGFW_directXinfo;
-
-	/*
-		RGFW stores a global instance of RGFW_directXinfo,
-		you can use this function to get a pointer the instance
-	*/
-	RGFWDEF RGFW_directXinfo* RGFW_getDirectXInfo(void);
-#endif
-
-	/*! Supporting functions */
-	RGFWDEF void RGFW_window_checkFPS(RGFW_window* win); /*!< updates fps / sets fps to cap (ran by RGFW_window_checkEvent)*/
-	RGFWDEF u64 RGFW_getTime(void); /* get time in seconds */
-	RGFWDEF u64 RGFW_getTimeNS(void); /* get time in nanoseconds */
-	RGFWDEF void RGFW_sleep(u64 microsecond); /* sleep for a set time */
-
-	typedef RGFW_ENUM(u8, RGFW_Key) {
-		RGFW_KEY_NULL = 0,
-		RGFW_Escape,
-		RGFW_F1,
-		RGFW_F2,
-		RGFW_F3,
-		RGFW_F4,
-		RGFW_F5,
-		RGFW_F6,
-		RGFW_F7,
-		RGFW_F8,
-		RGFW_F9,
-		RGFW_F10,
-		RGFW_F11,
-		RGFW_F12,
-
-		RGFW_Backtick,
-
-		RGFW_0,
-		RGFW_1,
-		RGFW_2,
-		RGFW_3,
-		RGFW_4,
-		RGFW_5,
-		RGFW_6,
-		RGFW_7,
-		RGFW_8,
-		RGFW_9,
-
-		RGFW_Minus,
-		RGFW_Equals,
-		RGFW_BackSpace,
-		RGFW_Tab,
-		RGFW_CapsLock,
-		RGFW_ShiftL,
-		RGFW_ControlL,
-		RGFW_AltL,
-		RGFW_SuperL,
-		RGFW_ShiftR,
-		RGFW_ControlR,
-		RGFW_AltR,
-		RGFW_SuperR,
-		RGFW_Space,
-
-		RGFW_a,
-		RGFW_b,
-		RGFW_c,
-		RGFW_d,
-		RGFW_e,
-		RGFW_f,
-		RGFW_g,
-		RGFW_h,
-		RGFW_i,
-		RGFW_j,
-		RGFW_k,
-		RGFW_l,
-		RGFW_m,
-		RGFW_n,
-		RGFW_o,
-		RGFW_p,
-		RGFW_q,
-		RGFW_r,
-		RGFW_s,
-		RGFW_t,
-		RGFW_u,
-		RGFW_v,
-		RGFW_w,
-		RGFW_x,
-		RGFW_y,
-		RGFW_z,
-
-		RGFW_Period,
-		RGFW_Comma,
-		RGFW_Slash,
-		RGFW_Bracket,
-		RGFW_CloseBracket,
-		RGFW_Semicolon,
-		RGFW_Return,
-		RGFW_Quote,
-		RGFW_BackSlash,
-
-		RGFW_Up,
-		RGFW_Down,
-		RGFW_Left,
-		RGFW_Right,
-
-		RGFW_Delete,
-		RGFW_Insert,
-		RGFW_End,
-		RGFW_Home,
-		RGFW_PageUp,
-		RGFW_PageDown,
-
-		RGFW_Numlock,
-		RGFW_KP_Slash,
-		RGFW_Multiply,
-		RGFW_KP_Minus,
-		RGFW_KP_1,
-		RGFW_KP_2,
-		RGFW_KP_3,
-		RGFW_KP_4,
-		RGFW_KP_5,
-		RGFW_KP_6,
-		RGFW_KP_7,
-		RGFW_KP_8,
-		RGFW_KP_9,
-		RGFW_KP_0,
-		RGFW_KP_Period,
-		RGFW_KP_Return,
-
-		final_key,
-	};
-
-	typedef RGFW_ENUM(u8, RGFW_mouseIcons) {
-		RGFW_MOUSE_NORMAL = 0,
-		RGFW_MOUSE_ARROW,
-		RGFW_MOUSE_IBEAM,
-		RGFW_MOUSE_CROSSHAIR,
-		RGFW_MOUSE_POINTING_HAND,
-		RGFW_MOUSE_RESIZE_EW,
-		RGFW_MOUSE_RESIZE_NS,
-		RGFW_MOUSE_RESIZE_NWSE,
-		RGFW_MOUSE_RESIZE_NESW,
-		RGFW_MOUSE_RESIZE_ALL,
-		RGFW_MOUSE_NOT_ALLOWED,
-	};
-
-#endif /* RGFW_HEADER */
-
-	/*
-	Example to get you started :
-
-	linux : gcc main.c -lX11 -lXcursor -lGL
-	windows : gcc main.c -lopengl32 -lshell32 -lgdi32
-	macos : gcc main.c -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo
-
-	#define RGFW_IMPLEMENTATION
-	#include "RGFW.h"
-
-	u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF,    0xFF, 0x00, 0x00, 0xFF,     0xFF, 0x00, 0x00, 0xFF,   0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,     0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF};
-
-	int main() {
-		RGFW_window* win = RGFW_createWindow("name", RGFW_RECT(500, 500, 500, 500), (u64)0);
-
-		RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4);
-
-		for (;;) {
-			RGFW_window_checkEvent(win); // NOTE: checking events outside of a while loop may cause input lag
-			if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape))
-				break;
-
-			RGFW_window_swapBuffers(win);
-
-			glClearColor(0xFF, 0XFF, 0xFF, 0xFF);
-			glClear(GL_COLOR_BUFFER_BIT);
-		}
-
-		RGFW_window_close(win);
-	}
-
-		compiling :
-
-		if you wish to compile the library all you have to do is create a new file with this in it
-
-		rgfw.c
-		#define RGFW_IMPLEMENTATION
-		#include "RGFW.h"
-
-		then you can use gcc (or whatever compile you wish to use) to compile the library into object file
-
-		ex. gcc -c RGFW.c -fPIC
-
-		after you compile the library into an object file, you can also turn the object file into an static or shared library
-
-		(commands ar and gcc can be replaced with whatever equivalent your system uses)
-		static : ar rcs RGFW.a RGFW.o
-		shared :
-			windows:
-				gcc -shared RGFW.o -lopengl32 -lshell32 -lgdi32 -o RGFW.dll
-			linux:
-				gcc -shared RGFW.o -lX11 -lXcursor -lGL -o RGFW.so
-			macos:
-				gcc -shared RGFW.o -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo
-	*/
-
-#ifdef RGFW_X11
-#define RGFW_OS_BASED_VALUE(l, w, m) l
-#endif
-#ifdef RGFW_WINDOWS
-#define RGFW_OS_BASED_VALUE(l, w, m) w
-#endif
-#ifdef RGFW_MACOS
-#define RGFW_OS_BASED_VALUE(l, w, m) m
-#endif
-#ifdef RGFW_IMPLEMENTATION
-
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <assert.h>
-
-/*
-RGFW_IMPLEMENTATION starts with generic RGFW defines
-
-This is the start of keycode data
-
-Why not use macros instead of the numbers itself?
-Windows -> Not all virtual keys are macros (VK_0 - VK_1, VK_a - VK_z)
-Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size
-MacOS -> windows and linux already don't have keycodes as macros, so there's no point
-*/
-
-	u8 RGFW_keycodes[] = {
-		[RGFW_OS_BASED_VALUE(49, 192, 50)] = RGFW_Backtick,
-
-		[RGFW_OS_BASED_VALUE(19, 0x30, 29)] = RGFW_0,
-		[RGFW_OS_BASED_VALUE(10, 0x31, 18)] = RGFW_1,
-		[RGFW_OS_BASED_VALUE(11, 0x32, 19)] = RGFW_2,
-		[RGFW_OS_BASED_VALUE(12, 0x33, 20)] = RGFW_3,
-		[RGFW_OS_BASED_VALUE(13, 0x34, 21)] = RGFW_4,
-		[RGFW_OS_BASED_VALUE(14, 0x35, 23)] = RGFW_5,
-		[RGFW_OS_BASED_VALUE(15, 0x36, 22)] = RGFW_6,
-		[RGFW_OS_BASED_VALUE(16, 0x37, 26)] = RGFW_7,
-		[RGFW_OS_BASED_VALUE(17, 0x38, 28)] = RGFW_8,
-		[RGFW_OS_BASED_VALUE(18, 0x39, 25)] = RGFW_9,
-
-		[RGFW_OS_BASED_VALUE(65, 0x20, 49)] = RGFW_Space,
-
-		[RGFW_OS_BASED_VALUE(38, 0x41, 0)] = RGFW_a,
-		[RGFW_OS_BASED_VALUE(56, 0x42, 11)] = RGFW_b,
-		[RGFW_OS_BASED_VALUE(54, 0x43, 8)] = RGFW_c,
-		[RGFW_OS_BASED_VALUE(40, 0x44, 2)] = RGFW_d,
-		[RGFW_OS_BASED_VALUE(26, 0x45, 14)] = RGFW_e,
-		[RGFW_OS_BASED_VALUE(41, 0x46, 3)] = RGFW_f,
-		[RGFW_OS_BASED_VALUE(42, 0x47, 5)] = RGFW_g,
-		[RGFW_OS_BASED_VALUE(43, 0x48, 4)] = RGFW_h,
-		[RGFW_OS_BASED_VALUE(31, 0x49, 34)] = RGFW_i,
-		[RGFW_OS_BASED_VALUE(44, 0x4A, 38)] = RGFW_j,
-		[RGFW_OS_BASED_VALUE(45, 0x4B, 40)] = RGFW_k,
-		[RGFW_OS_BASED_VALUE(46, 0x4C, 37)] = RGFW_l,
-		[RGFW_OS_BASED_VALUE(58, 0x4D, 46)] = RGFW_m,
-		[RGFW_OS_BASED_VALUE(57, 0x4E, 45)] = RGFW_n,
-		[RGFW_OS_BASED_VALUE(32, 0x4F, 31)] = RGFW_o,
-		[RGFW_OS_BASED_VALUE(33, 0x50, 35)] = RGFW_p,
-		[RGFW_OS_BASED_VALUE(24, 0x51, 12)] = RGFW_q,
-		[RGFW_OS_BASED_VALUE(27, 0x52, 15)] = RGFW_r,
-		[RGFW_OS_BASED_VALUE(39, 0x53, 1)] = RGFW_s,
-		[RGFW_OS_BASED_VALUE(28, 0x54, 17)] = RGFW_t,
-		[RGFW_OS_BASED_VALUE(30, 0x55, 32)] = RGFW_u,
-		[RGFW_OS_BASED_VALUE(55, 0x56, 9)] = RGFW_v,
-		[RGFW_OS_BASED_VALUE(25, 0x57, 13)] = RGFW_w,
-		[RGFW_OS_BASED_VALUE(53, 0x58, 7)] = RGFW_x,
-		[RGFW_OS_BASED_VALUE(29, 0x59, 16)] = RGFW_y,
-		[RGFW_OS_BASED_VALUE(52, 0x5A, 6)] = RGFW_z,
-
-		[RGFW_OS_BASED_VALUE(60, 190, 47)] = RGFW_Period,
-		[RGFW_OS_BASED_VALUE(59, 188, 43)] = RGFW_Comma,
-		[RGFW_OS_BASED_VALUE(61, 191, 44)] = RGFW_Slash,
-		[RGFW_OS_BASED_VALUE(34, 219, 33)] = RGFW_Bracket,
-		[RGFW_OS_BASED_VALUE(35, 221, 30)] = RGFW_CloseBracket,
-		[RGFW_OS_BASED_VALUE(47, 186, 41)] = RGFW_Semicolon,
-		[RGFW_OS_BASED_VALUE(48, 222, 39)] = RGFW_Quote,
-		[RGFW_OS_BASED_VALUE(51, 322, 42)] = RGFW_BackSlash,
-		
-		[RGFW_OS_BASED_VALUE(36, 0x0D, 36)] = RGFW_Return,
-		[RGFW_OS_BASED_VALUE(119, 0x2E, 118)] = RGFW_Delete,
-		[RGFW_OS_BASED_VALUE(77, 0x90, 72)] = RGFW_Numlock,
-		[RGFW_OS_BASED_VALUE(106, 0x6F, 82)] = RGFW_KP_Slash,
-		[RGFW_OS_BASED_VALUE(63, 0x6A, 76)] = RGFW_Multiply,
-		[RGFW_OS_BASED_VALUE(82, 0x6D, 67)] = RGFW_KP_Minus,
-		[RGFW_OS_BASED_VALUE(87, 0x61, 84)] = RGFW_KP_1,
-		[RGFW_OS_BASED_VALUE(88, 0x62, 85)] = RGFW_KP_2,
-		[RGFW_OS_BASED_VALUE(89, 0x63, 86)] = RGFW_KP_3,
-		[RGFW_OS_BASED_VALUE(83, 0x64, 87)] = RGFW_KP_4,
-		[RGFW_OS_BASED_VALUE(84, 0x65, 88)] = RGFW_KP_5,
-		[RGFW_OS_BASED_VALUE(85, 0x66, 89)] = RGFW_KP_6,
-		[RGFW_OS_BASED_VALUE(79, 0x67, 90)] = RGFW_KP_7,
-		[RGFW_OS_BASED_VALUE(80, 0x68, 92)] = RGFW_KP_8,
-		[RGFW_OS_BASED_VALUE(81, 0x69, 93)] = RGFW_KP_9,
-		[RGFW_OS_BASED_VALUE(90, 0x60, 83)] = RGFW_KP_0,
-		[RGFW_OS_BASED_VALUE(91, 0x6E, 65)] = RGFW_KP_Period,
-		[RGFW_OS_BASED_VALUE(104, 0x92, 77)] = RGFW_KP_Return,
-		
-		[RGFW_OS_BASED_VALUE(20, 189, 27)] = RGFW_Minus,
-		[RGFW_OS_BASED_VALUE(21, 187, 24)] = RGFW_Equals,
-		[RGFW_OS_BASED_VALUE(22, 8, 51)] = RGFW_BackSpace,
-		[RGFW_OS_BASED_VALUE(23, 0x09, 48)] = RGFW_Tab,
-		[RGFW_OS_BASED_VALUE(66, 20, 57)] = RGFW_CapsLock,
-		[RGFW_OS_BASED_VALUE(50, 0xA0, 56)] = RGFW_ShiftL,
-		[RGFW_OS_BASED_VALUE(37, 0x11, 59)] = RGFW_ControlL,
-		[RGFW_OS_BASED_VALUE(64, 164, 58)] = RGFW_AltL,
-		[RGFW_OS_BASED_VALUE(133, 0x5B, 55)] = RGFW_SuperL,
-		
-		#if !defined(RGFW_WINDOWS) && !defined(RGFW_MACOS)
-		[RGFW_OS_BASED_VALUE(105, 0x11, 59)] = RGFW_ControlR,
-		[RGFW_OS_BASED_VALUE(135, 0xA4, 55)] = RGFW_SuperR,
-		#endif
-
-		#if !defined(RGFW_MACOS)
-		[RGFW_OS_BASED_VALUE(62, 0x5C, 56)] = RGFW_ShiftR,
-		[RGFW_OS_BASED_VALUE(108, 165, 58)] = RGFW_AltR,
-		#endif
-
-		[RGFW_OS_BASED_VALUE(67, 0x70, 127)] = RGFW_F1,
-		[RGFW_OS_BASED_VALUE(68, 0x71, 121)] = RGFW_F2,
-		[RGFW_OS_BASED_VALUE(69, 0x72, 100)] = RGFW_F3,
-		[RGFW_OS_BASED_VALUE(70, 0x73, 119)] = RGFW_F4,
-		[RGFW_OS_BASED_VALUE(71, 0x74, 97)] = RGFW_F5,
-		[RGFW_OS_BASED_VALUE(72, 0x75, 98)] = RGFW_F6,
-		[RGFW_OS_BASED_VALUE(73, 0x76, 99)] = RGFW_F7,
-		[RGFW_OS_BASED_VALUE(74, 0x77, 101)] = RGFW_F8,
-		[RGFW_OS_BASED_VALUE(75, 0x78, 102)] = RGFW_F9,
-		[RGFW_OS_BASED_VALUE(76, 0x79, 110)] = RGFW_F10,
-		[RGFW_OS_BASED_VALUE(95, 0x7A, 104)] = RGFW_F11,
-		[RGFW_OS_BASED_VALUE(96, 0x7B, 112)] = RGFW_F12,
-		[RGFW_OS_BASED_VALUE(111, 0x26, 126)] = RGFW_Up,
-		[RGFW_OS_BASED_VALUE(116, 0x28, 125)] = RGFW_Down,
-		[RGFW_OS_BASED_VALUE(113, 0x25, 123)] = RGFW_Left,
-		[RGFW_OS_BASED_VALUE(114, 0x27, 124)] = RGFW_Right,
-		[RGFW_OS_BASED_VALUE(118, 0x2D, 115)] = RGFW_Insert,
-		[RGFW_OS_BASED_VALUE(115, 0x23, 120)] = RGFW_End,
-		[RGFW_OS_BASED_VALUE(112, 336, 117)] = RGFW_PageUp,
-		[RGFW_OS_BASED_VALUE(117, 325, 122)] = RGFW_PageDown,
-		[RGFW_OS_BASED_VALUE(9, 0x1B, 53)] = RGFW_Escape,
-		[RGFW_OS_BASED_VALUE(110, 0x24, 116)] = RGFW_Home,
-	};
-
-	typedef struct {
-		b8 current  : 1;
-		b8 prev  : 1;
-	} RGFW_keyState;
-
-	RGFW_keyState RGFW_keyboard[final_key] = { {0, 0} };
-	
-	RGFWDEF u32 RGFW_apiKeyCodeToRGFW(u32 keycode);
-
-	u32 RGFW_apiKeyCodeToRGFW(u32 keycode) {
-		if (keycode > sizeof(RGFW_keycodes) / sizeof(u8))
-			return 0;
-		
-		return RGFW_keycodes[keycode];
-	}
-
-	RGFWDEF void RGFW_resetKey(void);
-	void RGFW_resetKey(void) {
-		size_t len = final_key;
-		
-		size_t i; 
-		for (i = 0; i < len; i++) 
-			RGFW_keyboard[i].prev = 0;
-	}
-
-/*
-	this is the end of keycode data
-*/
-
-
-/* 
-	event callback defines start here
-*/
-
-
-	/*
-		These exist to avoid the 
-		if (func == NULL) check 
-		for (allegedly) better performance
-	*/
-	void RGFW_windowmovefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
-	void RGFW_windowresizefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
-	void RGFW_windowquitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); }
-	void RGFW_focusfuncEMPTY(RGFW_window* win, b8 inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);}
-	void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_vector point, b8 status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);}
-	void RGFW_mouseposfuncEMPTY(RGFW_window* win, RGFW_vector point) {RGFW_UNUSED(win); RGFW_UNUSED(point);}
-	void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_vector point) {RGFW_UNUSED(win); RGFW_UNUSED(point);}
-	void RGFW_windowrefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); }
-	void RGFW_keyfuncEMPTY(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(keycode); RGFW_UNUSED(keyName); RGFW_UNUSED(lockState); RGFW_UNUSED(pressed);}
-	void RGFW_mousebuttonfuncEMPTY(RGFW_window* win, u8 button, double scroll, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);}
-	void RGFW_jsButtonfuncEMPTY(RGFW_window* win, u16 joystick, u8 button, b8 pressed){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(button); RGFW_UNUSED(pressed); }
-	void RGFW_jsAxisfuncEMPTY(RGFW_window* win, u16 joystick, RGFW_vector axis[2], u8 axisesCount){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); }
-
-	#ifdef RGFW_ALLOC_DROPFILES
-	void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);}
-	#else
-	void RGFW_dndfuncEMPTY(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);}
-	#endif
-
-	RGFW_windowmovefunc RGFW_windowMoveCallback = RGFW_windowmovefuncEMPTY;
-	RGFW_windowresizefunc RGFW_windowResizeCallback = RGFW_windowresizefuncEMPTY;
-	RGFW_windowquitfunc RGFW_windowQuitCallback = RGFW_windowquitfuncEMPTY;
-	RGFW_mouseposfunc RGFW_mousePosCallback = RGFW_mouseposfuncEMPTY;
-	RGFW_windowrefreshfunc RGFW_windowRefreshCallback = RGFW_windowrefreshfuncEMPTY;
-	RGFW_focusfunc RGFW_focusCallback = RGFW_focusfuncEMPTY;
-	RGFW_mouseNotifyfunc RGFW_mouseNotifyCallBack = RGFW_mouseNotifyfuncEMPTY;
-	RGFW_dndfunc RGFW_dndCallback = RGFW_dndfuncEMPTY;
-	RGFW_dndInitfunc RGFW_dndInitCallback = RGFW_dndInitfuncEMPTY;
-	RGFW_keyfunc RGFW_keyCallback = RGFW_keyfuncEMPTY;
-	RGFW_mousebuttonfunc RGFW_mouseButtonCallback = RGFW_mousebuttonfuncEMPTY;
-	RGFW_jsButtonfunc RGFW_jsButtonCallback = RGFW_jsButtonfuncEMPTY;
-	RGFW_jsAxisfunc RGFW_jsAxisCallback = RGFW_jsAxisfuncEMPTY;
-
-	void RGFW_window_checkEvents(RGFW_window* win) { while (RGFW_window_checkEvent(win) != NULL && RGFW_window_shouldClose(win) == 0) { if (win->event.type == RGFW_quit) return; }}
-	
-	void RGFW_setWindowMoveCallback(RGFW_windowmovefunc func) { RGFW_windowMoveCallback = func; }
-	void RGFW_setWindowResizeCallback(RGFW_windowresizefunc func) { RGFW_windowResizeCallback = func; }
-	void RGFW_setWindowQuitCallback(RGFW_windowquitfunc func) { RGFW_windowQuitCallback = func; }
-	void RGFW_setMousePosCallback(RGFW_mouseposfunc func) { RGFW_mousePosCallback = func; }
-	void RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func) { RGFW_windowRefreshCallback = func; }
-	void RGFW_setFocusCallback(RGFW_focusfunc func) { RGFW_focusCallback = func; }
-	void RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func) { RGFW_mouseNotifyCallBack = func; }
-	void RGFW_setDndCallback(RGFW_dndfunc func) { RGFW_dndCallback = func; }
-	void RGFW_setDndInitCallback(RGFW_dndInitfunc func) { RGFW_dndInitCallback = func; }
-	void RGFW_setKeyCallback(RGFW_keyfunc func) { RGFW_keyCallback = func; }
-	void RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func) { RGFW_mouseButtonCallback = func; }
-	void RGFW_setjsButtonCallback(RGFW_jsButtonfunc func) { RGFW_jsButtonCallback = func; }
-	void RGFW_setjsAxisCallback(RGFW_jsAxisfunc func) { RGFW_jsAxisCallback = func; }
-/* 
-	no more event call back defines
-*/
-
-#define RGFW_ASSERT(check, str) {\
-	if (!(check)) { \
-		printf(str); \
-		assert(check); \
-	} \
-}
-
-	b8 RGFW_error = 0;
-	b8 RGFW_Error() { return RGFW_error; }
-
-#define SET_ATTRIB(a, v) { \
-    assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
-    attribs[index++] = a; \
-    attribs[index++] = v; \
-}
-	
-	RGFW_area RGFW_bufferSize = {0, 0};
-	void RGFW_setBufferSize(RGFW_area size) {
-		RGFW_bufferSize = size;
-	}
-
-
-	RGFWDEF RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args);
-
-	RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) {
-		RGFW_window* win = (RGFW_window*) RGFW_MALLOC(sizeof(RGFW_window)); /* make a new RGFW struct */
-
-#ifdef RGFW_ALLOC_DROPFILES
-		win->event.droppedFiles = (char**) RGFW_MALLOC(sizeof(char*) * RGFW_MAX_DROPS);
-		u32 i;
-		for (i = 0; i < RGFW_MAX_DROPS; i++)
-			win->event.droppedFiles[i] = (char*) RGFW_CALLOC(RGFW_MAX_PATH, sizeof(char));
-#endif
-
-		#ifndef RGFW_X11 
-		RGFW_area screenR = RGFW_getScreenSize();
-		#else
-		win->src.display = XOpenDisplay(NULL);
-		assert(win->src.display != NULL);
-
-		Screen* scrn = DefaultScreenOfDisplay((Display*)win->src.display);
-		RGFW_area screenR = RGFW_AREA(scrn->width, scrn->height);
-		#endif
-		
-		if (args & RGFW_FULLSCREEN)
-			rect = RGFW_RECT(0, 0, screenR.w, screenR.h);
-
-		if (args & RGFW_CENTER)
-			rect = RGFW_RECT((screenR.w - rect.w) / 2, (screenR.h - rect.h) / 2, rect.w, rect.h);
-
-		/* set and init the new window's data */
-		win->r = rect;
-		win->fpsCap = 0;
-		win->event.inFocus = 1;
-		win->event.droppedFilesCount = 0;
-		win->src.joystickCount = 0;
-		win->src.winArgs = 0;
-		win->event.lockState = 0;
-
-		return win;
-	}
-
-	#ifndef RGFW_NO_MONITOR
-	void RGFW_window_scaleToMonitor(RGFW_window* win) {
-		RGFW_monitor monitor = RGFW_window_getMonitor(win);
-
-		RGFW_window_resize(win, RGFW_AREA(((u32) monitor.scaleX) * win->r.w, ((u32) monitor.scaleX) * win->r.h));
-	}
-	#endif
-
-RGFW_window* RGFW_root = NULL;
-
-
-#define RGFW_HOLD_MOUSE			(1L<<2) /*!< hold the moues still */
-#define RGFW_MOUSE_LEFT 		(1L<<3) /* if mouse left the window */
-
-	void RGFW_clipboardFree(char* str) { RGFW_FREE(str); }
-	
-	b8 RGFW_mouseButtons[5] = { 0 };
-	b8 RGFW_mouseButtons_prev[5];
-
-	b8 RGFW_isMousePressed(RGFW_window* win, u8 button) {
-		assert(win != NULL);
-		return RGFW_mouseButtons[button] && (win != NULL) && win->event.inFocus; 
-	}
-	b8 RGFW_wasMousePressed(RGFW_window* win, u8 button) {
-		assert(win != NULL); 
-		return RGFW_mouseButtons_prev[button] && (win != NULL) && win->event.inFocus; 
-	}
-	b8 RGFW_isMouseHeld(RGFW_window* win, u8 button) {
-		return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));
-	}
-	b8 RGFW_isMouseReleased(RGFW_window* win, u8 button) {
-		return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));	
-	}
-
-	b8 RGFW_isPressed(RGFW_window* win, u8 key) {
-		assert(win != NULL);
-		return RGFW_keyboard[key].current && win->event.inFocus;
-	}
-
-	b8 RGFW_wasPressed(RGFW_window* win, u8 key) {
-		assert(win != NULL);
-		return RGFW_keyboard[key].prev && win->event.inFocus;
-	}
-
-	b8 RGFW_isHeld(RGFW_window* win, u8 key) {
-		return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));
-	}
-
-	b8 RGFW_isClicked(RGFW_window* win, u8 key) {
-		return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key));
-	}
-
-	b8 RGFW_isReleased(RGFW_window* win, u8 key) {
-		return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));	
-	}
-	
-	void RGFW_window_makeCurrent(RGFW_window* win) {
-		assert(win != NULL);
-
-#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX)
-		RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL);
-#endif
-
-#ifdef RGFW_OPENGL
-		RGFW_window_makeCurrent_OpenGL(win);
-#endif
-	}
-
-	void RGFW_window_setGPURender(RGFW_window* win, i8 set) {
-		if (!set && !(win->src.winArgs & RGFW_NO_GPU_RENDER))
-			win->src.winArgs |= RGFW_NO_GPU_RENDER;
-
-		else if (set && win->src.winArgs & RGFW_NO_GPU_RENDER)
-			win->src.winArgs ^= RGFW_NO_GPU_RENDER;
-	}
-
-	void RGFW_window_setCPURender(RGFW_window* win, i8 set) {
-		if (!set && !(win->src.winArgs & RGFW_NO_CPU_RENDER))
-			win->src.winArgs |= RGFW_NO_CPU_RENDER;
-
-		else if (set && win->src.winArgs & RGFW_NO_CPU_RENDER)
-			win->src.winArgs ^= RGFW_NO_CPU_RENDER;
-	}
-
-	void RGFW_window_maximize(RGFW_window* win) {
-		assert(win != NULL);
-
-		RGFW_area screen = RGFW_getScreenSize();
-
-		RGFW_window_move(win, RGFW_VECTOR(0, 0));
-		RGFW_window_resize(win, screen);
-	}
-
-	b8 RGFW_window_shouldClose(RGFW_window* win) {
-		assert(win != NULL);
-		return (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape));
-	}
-
-	void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; RGFW_windowQuitCallback(win); }
-
-	#ifndef RGFW_NO_MONITOR
-	void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) {
-		RGFW_window_move(win, RGFW_VECTOR(m.rect.x + win->r.x, m.rect.y + win->r.y));
-	}
-	#endif
-
-	RGFWDEF void RGFW_clipCursor(RGFW_rect);
-			
-	#if !defined(RGFW_WINDOWS) && !defined(RGFW_MACOS)	
-	void RGFW_clipCursor(RGFW_rect r) { RGFW_UNUSED(r) }
-	#endif
-
-	void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) {
-		if (!(win->src.winArgs & RGFW_HOLD_MOUSE)) {
-			RGFW_clipCursor(win->r);
-			win->src.winArgs |= RGFW_HOLD_MOUSE;
-		}
-		
-		if (!area.w && !area.h)
-			area = RGFW_AREA(win->r.w / 2, win->r.h / 2);
-		
-		#ifndef RGFW_MACOS
-		RGFW_window_moveMouse(win, RGFW_VECTOR(win->r.x + (area.w), win->r.y + (area.h)));
-		#endif
-	}
-
-	void RGFW_window_mouseUnhold(RGFW_window* win) {
-		if ((win->src.winArgs & RGFW_HOLD_MOUSE)) {
-			win->src.winArgs ^= RGFW_HOLD_MOUSE;
-
-			RGFW_clipCursor(RGFW_RECT(0, 0, 0, 0));
-		}
-	}
-
-	void RGFW_window_checkFPS(RGFW_window* win) {
-		u64 deltaTime = RGFW_getTimeNS() - win->event.frameTime;
-
-		u64 fps = round(1e+9 / deltaTime);
-		win->event.fps = fps;
-
-		if (win->fpsCap && fps > win->fpsCap) {
-			u64 frameTimeNS = 1e+9 / win->fpsCap;
-			u64 sleepTimeMS = (frameTimeNS - deltaTime) / 1e6;
-
-			if (sleepTimeMS > 0) {
-				RGFW_sleep(sleepTimeMS);
-				win->event.frameTime = 0;
-			}
-		}
-
-		win->event.frameTime = RGFW_getTimeNS();
-		
-		if (win->fpsCap == 0)
-			return;
-		
-		deltaTime = RGFW_getTimeNS() - win->event.frameTime2;
-		win->event.fps = round(1e+9 / deltaTime);
-		win->event.frameTime2 = RGFW_getTimeNS();
-	}
-	
-	u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { return win->src.jsPressed[c][button]; }
-	
-	#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
-		void RGFW_window_showMouse(RGFW_window* win, i8 show) {
-			static u8 RGFW_blk[] = { 0, 0, 0, 0 };
-			if (show == 0)
-				RGFW_window_setMouse(win, RGFW_blk, RGFW_AREA(1, 1), 4);
-			else
-				RGFW_window_setMouseDefault(win);
-		}
-	#endif
-
-	RGFWDEF void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock);	
-	void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) {
-		if (capital && !(win->event.lockState & RGFW_CAPSLOCK))
-			win->event.lockState |= RGFW_CAPSLOCK;
-		else if (!capital && (win->event.lockState & RGFW_CAPSLOCK))			
-			win->event.lockState ^= RGFW_CAPSLOCK;
-		
-		if (numlock && !(win->event.lockState & RGFW_NUMLOCK))
-			win->event.lockState |= RGFW_NUMLOCK;
-		else if (!numlock && (win->event.lockState & RGFW_NUMLOCK))
-			win->event.lockState ^= RGFW_NUMLOCK;
-	}
-
-	#if defined(RGFW_X11) || defined(RGFW_MACOS)
-		struct timespec;
-
-		int nanosleep(const struct timespec* duration, struct timespec* rem);
-		int clock_gettime(clockid_t clk_id, struct timespec* tp);
-		int setenv(const char *name, const char *value, int overwrite);
-
-		void RGFW_window_setDND(RGFW_window* win, b8 allow) {
-			if (allow && !(win->src.winArgs & RGFW_ALLOW_DND))
-				win->src.winArgs |= RGFW_ALLOW_DND;
-
-			else if (!allow && (win->src.winArgs & RGFW_ALLOW_DND))
-				win->src.winArgs ^= RGFW_ALLOW_DND;
-		}
-	#endif
-
-/*
-	graphics API spcific code (end of generic code)
-	starts here 
-*/
-
-
-/* 
-	OpenGL defines start here   (Normal, EGL, OSMesa)
-*/
-
-#if defined(RGFW_OPENGL) || defined(RGFW_EGL) || defined(RGFW_OSMESA)
-#ifndef __APPLE__
-#include <GL/gl.h>
-#else
-#ifndef GL_SILENCE_DEPRECATION
-#define GL_SILENCE_DEPRECATION
-#endif
-#include <OpenGL/gl.h>
-#include <OpenGL/OpenGL.h>
-#endif
-
-/* EGL, normal OpenGL only */
-#if !defined(RGFW_OSMESA) 
-	i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0;
-	
-	#ifndef RGFW_EGL
-	i32 RGFW_STENCIL = 8, RGFW_SAMPLES = 4, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0;
-	#else
-	i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0;
-	#endif
-
-
-	void RGFW_setGLStencil(i32 stencil) { RGFW_STENCIL = stencil; }
-	void RGFW_setGLSamples(i32 samples) { RGFW_SAMPLES = samples; }
-	void RGFW_setGLStereo(i32 stereo) { RGFW_STEREO = stereo; }
-	void RGFW_setGLAuxBuffers(i32 auxBuffers) { RGFW_AUX_BUFFERS = auxBuffers; }
-
-	void RGFW_setGLVersion(i32 major, i32 minor) {
-		RGFW_majorVersion = major;
-		RGFW_minorVersion = minor;
-	}
-
-	u8* RGFW_getMaxGLVersion(void) {
-		RGFW_window* dummy = RGFW_createWindow("dummy", RGFW_RECT(0, 0, 1, 1), 0);
-
-		const char* versionStr = (const char*) glGetString(GL_VERSION);
-
-		static u8 version[2];
-		version[0] = versionStr[0] - '0',
-			version[1] = versionStr[2] - '0';
-
-		RGFW_window_close(dummy);
-
-		return version;
-	}
-
-/* OPENGL normal only (no EGL / OSMesa) */
-#ifndef RGFW_EGL
-
-#define RGFW_GL_RENDER_TYPE 		RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE,    	0x2003,		73)
-#define RGFW_GL_ALPHA_SIZE 		RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE,       	0x201b,		11)
-#define RGFW_GL_DEPTH_SIZE 		RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE,       	0x2022,		12)
-#define RGFW_GL_DOUBLEBUFFER 		RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER,     	0x2011, 	5)   
-#define RGFW_GL_STENCIL_SIZE 		RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE,	 	0x2023,	13)
-#define RGFW_GL_SAMPLES			RGFW_OS_BASED_VALUE(GLX_SAMPLES, 		 	0x2042,	    55)
-#define RGFW_GL_STEREO 			RGFW_OS_BASED_VALUE(GLX_STEREO,	 		 	0x2012,			6)
-#define RGFW_GL_AUX_BUFFERS		RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS,	    0x2024,	7)
-
-#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
-#define RGFW_GL_DRAW 			RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE,	 	0x2001,					0)
-#define RGFW_GL_DRAW_TYPE 		RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE,     	0x2013,						0)
-#define RGFW_GL_USE_OPENGL		RGFW_OS_BASED_VALUE(GLX_USE_GL,				0x2010,						0)
-#define RGFW_GL_FULL_FORMAT		RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR,   	 	0x2027,						0)
-#define RGFW_GL_RED_SIZE		RGFW_OS_BASED_VALUE(GLX_RED_SIZE,         	0x2015,						0)
-#define RGFW_GL_GREEN_SIZE		RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE,       	0x2017,						0)
-#define RGFW_GL_BLUE_SIZE		RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 	 		0x2019,						0)
-#define RGFW_GL_USE_RGBA		RGFW_OS_BASED_VALUE(GLX_RGBA_BIT,   	 	0x202B,						0)
-#endif
-
-#ifdef RGFW_WINDOWS
-#define WGL_COLOR_BITS_ARB                        0x2014
-#define WGL_NUMBER_PIXEL_FORMATS_ARB 			0x2000
-#define WGL_CONTEXT_MAJOR_VERSION_ARB             0x2091
-#define WGL_CONTEXT_MINOR_VERSION_ARB             0x2092
-#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126
-#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
-#define WGL_SAMPLE_BUFFERS_ARB               0x2041
-#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
-#define WGL_PIXEL_TYPE_ARB                        0x2013
-#define WGL_TYPE_RGBA_ARB                         0x202B
-
-#define WGL_TRANSPARENT_ARB   					  0x200A
-#endif
-
-	static u32* RGFW_initAttribs(u32 useSoftware) {
-		RGFW_UNUSED(useSoftware);
-		static u32 attribs[] = {
-								#ifndef RGFW_MACOS
-								RGFW_GL_RENDER_TYPE,
-								RGFW_GL_FULL_FORMAT,
-								#endif
-								RGFW_GL_ALPHA_SIZE      , 8,
-								RGFW_GL_DEPTH_SIZE      , 24,
-								RGFW_GL_DOUBLEBUFFER    ,
-								#ifndef RGFW_MACOS
-								1,
-								#endif
-
-								#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
-								RGFW_GL_USE_OPENGL,		1,
-								RGFW_GL_DRAW, 1,
-								RGFW_GL_RED_SIZE        , 8,
-								RGFW_GL_GREEN_SIZE      , 8,
-								RGFW_GL_BLUE_SIZE       , 8,
-								RGFW_GL_DRAW_TYPE     , RGFW_GL_USE_RGBA,
-								#endif 
-
-								#ifdef RGFW_X11
-								GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
-								#endif
-
-								#ifdef RGFW_MACOS
-								72,
-								8, 24,
-								#endif
-
-								#ifdef RGFW_WINDOWS
-								WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
-								WGL_TRANSPARENT_ARB, TRUE,
-								WGL_COLOR_BITS_ARB,	 32,
-								#endif
-
-								0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-		};
-
-		size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13;
-
-#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
-		if (attVal) { \
-			attribs[index] = attrib;\
-			attribs[index + 1] = attVal;\
-			index += 2;\
-		}
-
-		RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_STENCIL);
-		RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_STEREO);
-		RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_AUX_BUFFERS);
-
-#ifndef RGFW_X11
-		RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_SAMPLES);
-#endif 
-
-#ifdef RGFW_MACOS
-		if (useSoftware) {
-			RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID);
-		} else {
-			attribs[index] = RGFW_GL_RENDER_TYPE;
-			index += 1;
-		}
-#endif
-
-#ifdef RGFW_MACOS
-		attribs[index] = 99;
-		attribs[index + 1] = 0x1000;
-
-		if (RGFW_majorVersion >= 4 || RGFW_majorVersion >= 3) {
-			attribs[index + 1] = (u32) ((RGFW_majorVersion >= 4) ? 0x4100 : 0x3200);
-		}
-
-#endif
-
-		RGFW_GL_ADD_ATTRIB(0, 0);
-
-		return attribs;
-	}
-
-/* EGL only (no OSMesa nor normal OPENGL) */
-#elif defined(RGFW_EGL)
-
-#include <EGL/egl.h>
-
-#if defined(RGFW_LINK_EGL)
-	typedef EGLBoolean(EGLAPIENTRY* PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*);
-
-	PFNEGLINITIALIZEPROC eglInitializeSource;
-	PFNEGLGETCONFIGSPROC eglGetConfigsSource;
-	PFNEGLCHOOSECONFIGPROC eglChooseConfigSource;
-	PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceSource;
-	PFNEGLCREATECONTEXTPROC eglCreateContextSource;
-	PFNEGLMAKECURRENTPROC eglMakeCurrentSource;
-	PFNEGLGETDISPLAYPROC eglGetDisplaySource;
-	PFNEGLSWAPBUFFERSPROC eglSwapBuffersSource;
-	PFNEGLSWAPINTERVALPROC eglSwapIntervalSource;
-	PFNEGLBINDAPIPROC eglBindAPISource;
-	PFNEGLDESTROYCONTEXTPROC eglDestroyContextSource;
-	PFNEGLTERMINATEPROC eglTerminateSource;
-	PFNEGLDESTROYSURFACEPROC eglDestroySurfaceSource;
-
-#define eglInitialize eglInitializeSource
-#define eglGetConfigs eglGetConfigsSource
-#define eglChooseConfig eglChooseConfigSource
-#define eglCreateWindowSurface eglCreateWindowSurfaceSource
-#define eglCreateContext eglCreateContextSource
-#define eglMakeCurrent eglMakeCurrentSource
-#define eglGetDisplay eglGetDisplaySource
-#define eglSwapBuffers eglSwapBuffersSource
-#define eglSwapInterval eglSwapIntervalSource
-#define eglBindAPI eglBindAPISource
-#define eglDestroyContext eglDestroyContextSource
-#define eglTerminate eglTerminateSource
-#define eglDestroySurface eglDestroySurfaceSource;
-#endif
-
-
-#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098
-#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb
-
-#ifndef RGFW_GL_ADD_ATTRIB
-#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
-	if (attVal) { \
-		attribs[index] = attrib;\
-		attribs[index + 1] = attVal;\
-		index += 2;\
-	}
-#endif
-
-
-	void RGFW_createOpenGLContext(RGFW_window* win) {
-#if defined(RGFW_LINK_EGL)
-		eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize");
-		eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs");
-		eglChooseConfigSource = (PFNEGLCHOOSECONFIGPROC) eglGetProcAddress("eglChooseConfig");
-		eglCreateWindowSurfaceSource = (PFNEGLCREATEWINDOWSURFACEPROC) eglGetProcAddress("eglCreateWindowSurface");
-		eglCreateContextSource = (PFNEGLCREATECONTEXTPROC) eglGetProcAddress("eglCreateContext");
-		eglMakeCurrentSource = (PFNEGLMAKECURRENTPROC) eglGetProcAddress("eglMakeCurrent");
-		eglGetDisplaySource = (PFNEGLGETDISPLAYPROC) eglGetProcAddress("eglGetDisplay");
-		eglSwapBuffersSource = (PFNEGLSWAPBUFFERSPROC) eglGetProcAddress("eglSwapBuffers");
-		eglSwapIntervalSource = (PFNEGLSWAPINTERVALPROC) eglGetProcAddress("eglSwapInterval");
-		eglBindAPISource = (PFNEGLBINDAPIPROC) eglGetProcAddress("eglBindAPI");
-		eglDestroyContextSource = (PFNEGLDESTROYCONTEXTPROC) eglGetProcAddress("eglDestroyContext");
-		eglTerminateSource = (PFNEGLTERMINATEPROC) eglGetProcAddress("eglTerminate");
-		eglDestroySurfaceSource = (PFNEGLDESTROYSURFACEPROC) eglGetProcAddress("eglDestroySurface");
-#endif /* RGFW_LINK_EGL */
-
-		#ifdef RGFW_WINDOWS
-		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.hdc);
-		#else
-		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display);
-		#endif
-
-		EGLint major, minor;
-
-		eglInitialize(win->src.EGL_display, &major, &minor);
-
-		#ifndef EGL_OPENGL_ES1_BIT
-		#define EGL_OPENGL_ES1_BIT 0x1
-		#endif
-
-		EGLint egl_config[] = {
-			EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
-			EGL_RENDERABLE_TYPE,
-			#ifdef RGFW_OPENGL_ES1
-			EGL_OPENGL_ES1_BIT,
-			#elif defined(RGFW_OPENGL_ES3)
-			EGL_OPENGL_ES3_BIT,
-			#elif defined(RGFW_OPENGL_ES2)
-			EGL_OPENGL_ES2_BIT,
-			#else
-			EGL_OPENGL_BIT,
-			#endif
-			EGL_NONE, EGL_NONE
-		};
-
-		EGLConfig config;
-		EGLint numConfigs;
-		eglChooseConfig(win->src.EGL_display, egl_config, &config, 1, &numConfigs);
-
-
-		win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL);
-
-		EGLint attribs[] = {
-			EGL_CONTEXT_CLIENT_VERSION,
-			#ifdef RGFW_OPENGL_ES1
-			1,
-			#else
-			2,
-			#endif
-			EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE
-		};
-
-		size_t index = 4;
-		RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_STENCIL);
-		RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_SAMPLES);
-
-		if (RGFW_majorVersion) {
-			attribs[1] = RGFW_majorVersion;
-			RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
-			RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_majorVersion);
-			RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_minorVersion);
-		}
-
-		#if defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)
-		eglBindAPI(EGL_OPENGL_ES_API);
-		#else
-		eglBindAPI(EGL_OPENGL_API);		
-		#endif
-
-      	win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs);
-
-		eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context);
-		eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
-	}
-
-	#ifdef RGFW_APPLE
-	void* RGFWnsglFramework = NULL;
-	#elif defined(RGFW_WINDOWS)
-	static HMODULE wglinstance = NULL;
-	#endif
-
-	void* RGFW_getProcAddress(const char* procname) { 
-		#if defined(RGFW_WINDOWS)
-			void* proc = (void*) GetProcAddress(wglinstance, procname); 
-
-			if (proc)
-				return proc;
-		#endif
-
-		return (void*) eglGetProcAddress(procname); 
-	}
-
-	void RGFW_closeEGL(RGFW_window* win) {
-		eglDestroySurface(win->src.EGL_display, win->src.EGL_surface);
-		eglDestroyContext(win->src.EGL_display, win->src.EGL_context);
-
-		eglTerminate(win->src.EGL_display);
-	}
-
-	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
-		assert(win != NULL);
-		
-		eglSwapInterval(win->src.EGL_display, swapInterval);
-
-		win->fpsCap = (swapInterval == 1) ? 0 : swapInterval;
-
-	}
-#endif /* RGFW_EGL */
-
-/* 
-	end of RGFW_EGL defines
-*/
-
-/* OPENGL Normal / EGL defines only (no OS MESA)  Ends here */
-
-#elif defined(RGFW_OSMESA) /* OSmesa only */
-RGFWDEF void RGFW_OSMesa_reorganize(void);
-
-/* reorganize buffer for osmesa */
-void RGFW_OSMesa_reorganize(void) {
-	u8* row = (u8*) RGFW_MALLOC(win->r.w * 3);
-
-	i32 half_height = win->r.h / 2;
-	i32 stride = win->r.w * 3;
-
-	i32 y;
-	for (y = 0; y < half_height; ++y) {
-		i32 top_offset = y * stride;
-		i32 bottom_offset = (win->r.h - y - 1) * stride;
-		memcpy(row, win->buffer + top_offset, stride);
-		memcpy(win->buffer + top_offset, win->buffer + bottom_offset, stride);
-		memcpy(win->buffer + bottom_offset, row, stride);
-	}
-
-	RGFW_FREE(row);
-}
-#endif /* RGFW_OSMesa */
-
-#endif /* RGFW_GL (OpenGL, EGL, OSMesa )*/
-
-/*
-This is where OS specific stuff starts
-*/
-
-
-/*
-
-
-Start of Linux / Unix defines
-
-
-*/
-
-#ifdef RGFW_X11
-#ifndef RGFW_NO_X11_CURSOR
-#include <X11/Xcursor/Xcursor.h>
-#endif
-#include <dlfcn.h>
-
-#ifndef RGFW_NO_DPI
-#include <X11/extensions/Xrandr.h>
-#include <X11/Xresource.h>
-#endif
-
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
-#include <X11/keysymdef.h>
-#include <unistd.h>
-
-#include <X11/XKBlib.h> /* for converting keycode to string */
-#include <X11/cursorfont.h> /* for hiding */
-#include <X11/extensions/shapeconst.h>
-#include <X11/extensions/shape.h>
-
-#include <limits.h> /* for data limits (mainly used in drag and drop functions) */
-#include <fcntl.h>
-
-#ifdef __linux__
-#include <linux/joystick.h>
-#endif
-
-	u8 RGFW_mouseIconSrc[] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor};  
-	/*atoms needed for drag and drop*/
-	Atom XdndAware, XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XdndActionMove, XdndActionLink, XdndActionAsk, XdndActionPrivate;
-
-	Atom wm_delete_window = 0;
-
-#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
-	typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int);
-	typedef void (*PFN_XcursorImageDestroy)(XcursorImage*);
-	typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*);
-#endif
-#ifdef RGFW_OPENGL
-	typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
-#endif
-
-#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
-	PFN_XcursorImageLoadCursor XcursorImageLoadCursorSrc = NULL;
-	PFN_XcursorImageCreate XcursorImageCreateSrc = NULL;
-	PFN_XcursorImageDestroy XcursorImageDestroySrc = NULL;
-
-#define XcursorImageLoadCursor XcursorImageLoadCursorSrc
-#define XcursorImageCreate XcursorImageCreateSrc
-#define XcursorImageDestroy XcursorImageDestroySrc
-
-	void* X11Cursorhandle = NULL;
-#endif
-
-	u32 RGFW_windowsOpen = 0;
-
-#ifdef RGFW_OPENGL
-	void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); }
-#endif
-
-	RGFWDEF void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi);
-	void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-		if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
-			RGFW_bufferSize = RGFW_getScreenSize();
-		
-		win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4);
-
-		#ifdef RGFW_OSMESA
-				win->src.rSurf = OSMesaCreateContext(OSMESA_RGBA, NULL);
-				OSMesaMakeCurrent(win->src.rSurf, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
-		#endif
-
-		win->src.bitmap = XCreateImage(
-			win->src.display, vi->visual,
-			vi->depth,
-			ZPixmap, 0, NULL, RGFW_bufferSize.w, RGFW_bufferSize.h,
-			32, 0
-		);
-
-		win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL);
-
-		#else
-		RGFW_UNUSED(win); /* if buffer rendering is not being used */
-		RGFW_UNUSED(vi)
-		#endif
-	}
-
-
-
-	void RGFW_window_setBorder(RGFW_window* win, u8 border) {
-		static Atom _MOTIF_WM_HINTS = 0;
-		if (_MOTIF_WM_HINTS == 0 )
-			_MOTIF_WM_HINTS = XInternAtom(win->src.display, "_MOTIF_WM_HINTS", False);
-		
-		struct __x11WindowHints {
-			unsigned long flags, functions, decorations, status;
-			long input_mode;
-		} hints;
-		hints.flags = (1L << 1);
-		hints.decorations = !border;
-
-		XChangeProperty(
-			win->src.display, win->src.window,
-			_MOTIF_WM_HINTS, _MOTIF_WM_HINTS,
-			32, PropModeReplace, (u8*)&hints, 5
-		);
-	}
-
-	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
-#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
-		if (X11Cursorhandle == NULL) {
-#if defined(__CYGWIN__)
-			X11Cursorhandle = dlopen("libXcursor-1.so", RTLD_LAZY | RTLD_LOCAL);
-#elif defined(__OpenBSD__) || defined(__NetBSD__)
-			X11Cursorhandle = dlopen("libXcursor.so", RTLD_LAZY | RTLD_LOCAL);
-#else
-			X11Cursorhandle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_LOCAL);
-#endif
-
-			XcursorImageCreateSrc = (PFN_XcursorImageCreate) dlsym(X11Cursorhandle, "XcursorImageCreate");
-			XcursorImageDestroySrc = (PFN_XcursorImageDestroy) dlsym(X11Cursorhandle, "XcursorImageDestroy");
-			XcursorImageLoadCursorSrc = (PFN_XcursorImageLoadCursor) dlsym(X11Cursorhandle, "XcursorImageLoadCursor");
-		}
-#endif
-
-		XInitThreads(); /* init X11 threading*/
-
-		if (args & RGFW_OPENGL_SOFTWARE)
-			setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
-
-		RGFW_window* win = RGFW_window_basic_init(rect, args);
-
-		u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /* X11 events accepted*/
-
-#ifdef RGFW_OPENGL
-		u32* visual_attribs = RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE);
-		i32 fbcount;
-		GLXFBConfig* fbc = glXChooseFBConfig((Display*) win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount);
-
-		i32 best_fbc = -1;
-
-		if (fbcount == 0) {
-			printf("Failed to find any valid GLX configs\n");
-			return NULL;
-		}
-
-		u32 i;
-		for (i = 0; i < (u32)fbcount; i++) {
-			XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, fbc[i]);
-			if (vi == NULL)
-				continue;
-
-			XFree(vi);
-
-			i32 samp_buf, samples;
-			glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
-			glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLES, &samples);
-			if ((best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES || best_fbc == -1)) {
-				best_fbc = i;
-			}
-		}
-
-		if (best_fbc == -1) {
-			printf("Failed to get a valid GLX visual\n");
-			return NULL;
-		}
-
-		GLXFBConfig bestFbc = fbc[best_fbc];
-
-		/* Get a visual */
-		XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, bestFbc);
-
-		XFree(fbc);
-
-		if (args & RGFW_TRANSPARENT_WINDOW) {
-			XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /* for RGBA backgrounds*/
-		}
-#else
-		XVisualInfo viNorm;
-
-		viNorm.visual = DefaultVisual((Display*) win->src.display, DefaultScreen((Display*) win->src.display));
-		
-		viNorm.depth = 0;
-		XVisualInfo* vi = &viNorm;
-		
-		XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /* for RGBA backgrounds*/
-#endif
-		/* make X window attrubutes*/
-		XSetWindowAttributes swa;
-		Colormap cmap;
-
-		swa.colormap = cmap = XCreateColormap((Display*) win->src.display,
-			DefaultRootWindow(win->src.display),
-			vi->visual, AllocNone);
-
-		swa.background_pixmap = None;
-		swa.border_pixel = 0;
-		swa.event_mask = event_mask;
-		
-		swa.background_pixel = 0;
-
-		/* create the window*/
-		win->src.window = XCreateWindow((Display*) win->src.display, DefaultRootWindow((Display*) win->src.display), win->r.x, win->r.y, win->r.w, win->r.h,
-			0, vi->depth, InputOutput, vi->visual,
-			CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa);
-
-		XFreeColors((Display*) win->src.display, cmap, NULL, 0, 0);
-
-		#ifdef RGFW_OPENGL
-		XFree(vi);
-		#endif
-
-		// In your .desktop app, if you set the property
-		// StartupWMClass=RGFW that will assoicate the launcher icon
-		// with your application - robrohan 
-		XClassHint *hint = XAllocClassHint();
-		assert(hint != NULL);
-		hint->res_class = "RGFW";
-		hint->res_name = (char*)name; // just use the window name as the app name
-		XSetClassHint((Display*) win->src.display, win->src.window, hint);
-		XFree(hint);
-
-		if ((args & RGFW_NO_INIT_API) == 0) {
-#ifdef RGFW_OPENGL
-		i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 };
-		context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB;
-		context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
-		
-		if (RGFW_majorVersion || RGFW_minorVersion) {
-			context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB;
-			context_attribs[3] = RGFW_majorVersion;
-			context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB;
-			context_attribs[5] = RGFW_minorVersion;
-		}
-
-		glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
-		glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
-			glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB");
-
-		GLXContext ctx = NULL;
-
-		if (RGFW_root != NULL)
-			ctx = RGFW_root->src.rSurf;
-
-		win->src.rSurf = glXCreateContextAttribsARB((Display*) win->src.display, bestFbc, ctx, True, context_attribs);
-#endif
-		if (RGFW_root == NULL)
-			RGFW_root = win;
-
-		RGFW_init_buffer(win, vi);
-		}
-		
-
-		#ifndef RGFW_NO_MONITOR
-		if (args & RGFW_SCALE_TO_MONITOR)
-			RGFW_window_scaleToMonitor(win);
-		#endif
-
-		if (args & RGFW_NO_RESIZE) { /* make it so the user can't resize the window*/
-			XSizeHints* sh = XAllocSizeHints();
-			sh->flags = (1L << 4) | (1L << 5);
-			sh->min_width = sh->max_width = win->r.w;
-			sh->min_height = sh->max_height = win->r.h;
-
-			XSetWMSizeHints((Display*) win->src.display, (Drawable) win->src.window, sh, XA_WM_NORMAL_HINTS);
-			XFree(sh);
-		}
-
-		if (args & RGFW_NO_BORDER) {
-			RGFW_window_setBorder(win, 0);
-		}
-
-		XSelectInput((Display*) win->src.display, (Drawable) win->src.window, event_mask); /* tell X11 what events we want*/
-
-		/* make it so the user can't close the window until the program does*/
-		if (wm_delete_window == 0)
-			wm_delete_window = XInternAtom((Display*) win->src.display, "WM_DELETE_WINDOW", False);
-
-		XSetWMProtocols((Display*) win->src.display, (Drawable) win->src.window, &wm_delete_window, 1);
-
-		/* connect the context to the window*/
-#ifdef RGFW_OPENGL
-		if ((args & RGFW_NO_INIT_API) == 0)
-			glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.rSurf);
-#endif
-
-		/* set the background*/
-		XStoreName((Display*) win->src.display, (Drawable) win->src.window, name); /* set the name*/
-
-		XMapWindow((Display*) win->src.display, (Drawable) win->src.window);						  /* draw the window*/
-		XMoveWindow((Display*) win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /* move the window to it's proper cords*/
-
-		if (args & RGFW_ALLOW_DND) { /* init drag and drop atoms and turn on drag and drop for this window */
-			win->src.winArgs |= RGFW_ALLOW_DND;
-
-			XdndAware = XInternAtom((Display*) win->src.display, "XdndAware", False);
-			XdndTypeList = XInternAtom((Display*) win->src.display, "XdndTypeList", False);
-			XdndSelection = XInternAtom((Display*) win->src.display, "XdndSelection", False);
-
-			/* client messages */
-			XdndEnter = XInternAtom((Display*) win->src.display, "XdndEnter", False);
-			XdndPosition = XInternAtom((Display*) win->src.display, "XdndPosition", False);
-			XdndStatus = XInternAtom((Display*) win->src.display, "XdndStatus", False);
-			XdndLeave = XInternAtom((Display*) win->src.display, "XdndLeave", False);
-			XdndDrop = XInternAtom((Display*) win->src.display, "XdndDrop", False);
-			XdndFinished = XInternAtom((Display*) win->src.display, "XdndFinished", False);
-
-			/* actions */
-			XdndActionCopy = XInternAtom((Display*) win->src.display, "XdndActionCopy", False);
-			XdndActionMove = XInternAtom((Display*) win->src.display, "XdndActionMove", False);
-			XdndActionLink = XInternAtom((Display*) win->src.display, "XdndActionLink", False);
-			XdndActionAsk = XInternAtom((Display*) win->src.display, "XdndActionAsk", False);
-			XdndActionPrivate = XInternAtom((Display*) win->src.display, "XdndActionPrivate", False);
-			const Atom version = 5;
-
-			XChangeProperty((Display*) win->src.display, (Window) win->src.window,
-				XdndAware, 4, 32,
-				PropModeReplace, (u8*) &version, 1); /* turns on drag and drop */
-		}
-
-		#ifdef RGFW_EGL
-			if ((args & RGFW_NO_INIT_API) == 0)
-				RGFW_createOpenGLContext(win);
-		#endif
-
-		RGFW_window_setMouseDefault(win);
-
-		RGFW_windowsOpen++;
-
-		return win; /*return newly created window*/
-	}
-
-	RGFW_area RGFW_getScreenSize(void) {
-		assert(RGFW_root != NULL);
-
-		Screen* scrn = DefaultScreenOfDisplay((Display*) RGFW_root->src.display);
-		return RGFW_AREA(scrn->width, scrn->height);
-	}
-
-	RGFW_vector RGFW_getGlobalMousePoint(void) {
-		assert(RGFW_root != NULL);
-
-		RGFW_vector RGFWMouse;
-
-		i32 x, y;
-		u32 z;
-		Window window1, window2;
-		XQueryPointer((Display*) RGFW_root->src.display, XDefaultRootWindow((Display*) RGFW_root->src.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z);
- 
-		return RGFWMouse;
-	}
-
-	RGFW_vector RGFW_window_getMousePoint(RGFW_window* win) {
-		assert(win != NULL);
-
-		RGFW_vector RGFWMouse;
-
-		i32 x, y;
-		u32 z;
-		Window window1, window2;
-		XQueryPointer((Display*) win->src.display, win->src.window, &window1, &window2, &x, &y, &RGFWMouse.x, &RGFWMouse.y, &z);
-
-		return RGFWMouse;
-	}
-
-	typedef struct XDND {
-		long source, version;
-		i32 format;
-	} XDND; /* data structure for xdnd events */
-	XDND xdnd;
-
-	int xAxis = 0, yAxis = 0;
-
-	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
-		assert(win != NULL);
-
-		if (win->event.type == 0) 
-			RGFW_resetKey();
-
-		if (win->event.type == RGFW_quit) {
-			return NULL;
-		}
-
-		win->event.type = 0;
-
-#ifdef __linux__
-		{
-			u8 i;
-			for (i = 0; i < win->src.joystickCount; i++) {
-				struct js_event e;
-
-
-				if (win->src.joysticks[i] == 0)
-					continue;
-
-				i32 flags = fcntl(win->src.joysticks[i], F_GETFL, 0);
-				fcntl(win->src.joysticks[i], F_SETFL, flags | O_NONBLOCK);
-
-				ssize_t bytes;
-				while ((bytes = read(win->src.joysticks[i], &e, sizeof(e))) > 0) {
-					switch (e.type) {
-					case JS_EVENT_BUTTON:
-						win->event.type = e.value ? RGFW_jsButtonPressed : RGFW_jsButtonReleased;
-						win->event.button = e.number;
-						win->src.jsPressed[i][e.number] = e.value;
-						RGFW_jsButtonCallback(win, i, e.number, e.value);
-						return &win->event;
-					case JS_EVENT_AXIS:
-						ioctl(win->src.joysticks[i], JSIOCGAXES, &win->event.axisesCount);
-
-						if ((e.number == 0 || e.number % 2) && e.number != 1)
-							xAxis = e.value;
-						else
-							yAxis = e.value;
-
-						win->event.axis[e.number / 2].x = xAxis;
-						win->event.axis[e.number / 2].y = yAxis;
-						win->event.type = RGFW_jsAxisMove;
-						win->event.joystick = i;
-						RGFW_jsAxisCallback(win, i, win->event.axis, win->event.axisesCount);
-						return &win->event;
-
-					default: break;
-					}
-				}
-			}
-		}
-#endif
-
-		XEvent E; /* raw X11 event */
-
-		/* if there is no unread qued events, get a new one */
-		if (XEventsQueued((Display*) win->src.display, QueuedAlready) + XEventsQueued((Display*) win->src.display, QueuedAfterReading) && win->event.type != RGFW_quit)
-			XNextEvent((Display*) win->src.display, &E);
-		else {
-			return NULL;
-		}
-
-		u32 i;
-		win->event.type = 0;
-
-
-		switch (E.type) {
-		case KeyPress:
-		case KeyRelease:
-			/* check if it's a real key release */
-			if (E.type == KeyRelease && XEventsQueued((Display*) win->src.display, QueuedAfterReading)) { /* get next event if there is one*/
-				XEvent NE;
-				XPeekEvent((Display*) win->src.display, &NE);
-
-				if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same*/
-					break;
-			}
-
-			/* set event key data */
-			KeySym sym = XkbKeycodeToKeysym((Display*) win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0);
-			win->event.keyCode = RGFW_apiKeyCodeToRGFW(E.xkey.keycode);
-			
-			char* str = XKeysymToString(sym);
-			if (str != NULL)
-				strncpy(win->event.keyName, str, 16);
-
-			win->event.keyName[15] = '\0';		
-
-			RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode);
-			
-			/* get keystate data */
-			win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased;
-
-			XKeyboardState keystate;
-			XGetKeyboardControl((Display*) win->src.display, &keystate);
-
-			RGFW_updateLockState(win, (keystate.led_mask & 1), (keystate.led_mask & 2));
-
-			RGFW_keyboard[win->event.keyCode].current = (E.type == KeyPress);
-			RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, (E.type == KeyPress));
-			break;
-
-		case ButtonPress:
-		case ButtonRelease:
-			win->event.type = E.type; // the events match 
-			
-			switch(win->event.button) {
-				case RGFW_mouseScrollUp:
-					win->event.scroll = 1;
-					break;
-				case RGFW_mouseScrollDown:
-					win->event.scroll = -1;
-					break;
-				default: break;
-			}
-
-			win->event.button = E.xbutton.button;
-			RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-			RGFW_mouseButtons[win->event.button] = (E.type == ButtonPress);
-			RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress));
-			break;
-
-		case MotionNotify:
-			win->event.point.x = E.xmotion.x;
-			win->event.point.y = E.xmotion.y;
-			win->event.type = RGFW_mousePosChanged;
-			RGFW_mousePosCallback(win, win->event.point);
-			break;
-		
-		case Expose:
-			win->event.type = RGFW_windowRefresh;
-			RGFW_windowRefreshCallback(win);
-			break;
-
-		case ClientMessage:
-			/* if the client closed the window*/
-			if (E.xclient.data.l[0] == (i64) wm_delete_window) {
-				win->event.type = RGFW_quit;
-				RGFW_windowQuitCallback(win);
-				break;
-			}
-			
-			/* reset DND values */
-			if (win->event.droppedFilesCount) {
-				for (i = 0; i < win->event.droppedFilesCount; i++)
-					win->event.droppedFiles[i][0] = '\0';
-			}
-
-			win->event.droppedFilesCount = 0;
-
-			/*
-				much of this event (drag and drop code) is source from glfw
-			*/
-
-			if ((win->src.winArgs & RGFW_ALLOW_DND) == 0)
-				break;
-
-			if (E.xclient.message_type == XdndEnter) {
-				u64 count;
-				Atom* formats;
-				Atom real_formats[6];
-
-				Bool list = E.xclient.data.l[1] & 1;
-
-				xdnd.source = E.xclient.data.l[0];
-				xdnd.version = E.xclient.data.l[1] >> 24;
-				xdnd.format = None;
-
-				if (xdnd.version > 5)
-					break;
-
-				if (list) {
-					Atom actualType;
-					i32 actualFormat;
-					u64 bytesAfter;
-
-					XGetWindowProperty((Display*) win->src.display,
-						xdnd.source,
-						XdndTypeList,
-						0,
-						LONG_MAX,
-						False,
-						4,
-						&actualType,
-						&actualFormat,
-						(unsigned long*) &count,
-						(unsigned long*) &bytesAfter,
-						(u8**) &formats);
-				} else {
-					count = 0;
-
-					if (E.xclient.data.l[2] != None)
-						real_formats[count++] = E.xclient.data.l[2];
-					if (E.xclient.data.l[3] != None)
-						real_formats[count++] = E.xclient.data.l[3];
-					if (E.xclient.data.l[4] != None)
-						real_formats[count++] = E.xclient.data.l[4];
-					
-					formats = real_formats;
-				}
-
-				u32 i;
-				for (i = 0; i < count; i++) {
-					char* name = XGetAtomName((Display*) win->src.display, formats[i]);
-
-					char* links[2] = { (char*) (const char*) "text/uri-list", (char*) (const char*) "text/plain" };
-					for (; 1; name++) {
-						u32 j;
-						for (j = 0; j < 2; j++) {
-							if (*links[j] != *name) {
-								links[j] = (char*) (const char*) "\1";
-								continue;
-							}
-
-							if (*links[j] == '\0' && *name == '\0')
-								xdnd.format = formats[i];
-
-							if (*links[j] != '\0' && *links[j] != '\1')
-								links[j]++;
-						}
-
-						if (*name == '\0')
-							break;
-					}
-				}
-
-				if (list) {
-					XFree(formats);
-				}
-
-				break;
-			}
-			if (E.xclient.message_type == XdndPosition) {
-				const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff;
-				const i32 yabs = (E.xclient.data.l[2]) & 0xffff;
-				Window dummy;
-				i32 xpos, ypos;
-
-				if (xdnd.version > 5)
-					break;
-
-				XTranslateCoordinates((Display*) win->src.display,
-					XDefaultRootWindow((Display*) win->src.display),
-					(Window) win->src.window,
-					xabs, yabs,
-					&xpos, &ypos,
-					&dummy);
-
-				win->event.point.x = xpos;
-				win->event.point.y = ypos;
-
-				XEvent reply = { ClientMessage };
-				reply.xclient.window = xdnd.source;
-				reply.xclient.message_type = XdndStatus;
-				reply.xclient.format = 32;
-				reply.xclient.data.l[0] = (long) win->src.window;
-				reply.xclient.data.l[2] = 0;
-				reply.xclient.data.l[3] = 0;
-
-				if (xdnd.format) {
-					reply.xclient.data.l[1] = 1;
-					if (xdnd.version >= 2)
-						reply.xclient.data.l[4] = XdndActionCopy;
-				}
-
-				XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply);
-				XFlush((Display*) win->src.display);
-				break;
-			}
-
-			if (E.xclient.message_type != XdndDrop)
-				break;
-
-			if (xdnd.version > 5)
-				break;
-
-			win->event.type = RGFW_dnd_init;
-
-			if (xdnd.format) {
-				Time time = CurrentTime;
-
-				if (xdnd.version >= 1)
-					time = E.xclient.data.l[2];
-
-				XConvertSelection((Display*) win->src.display,
-					XdndSelection,
-					xdnd.format,
-					XdndSelection,
-					(Window) win->src.window,
-					time);
-			} else if (xdnd.version >= 2) {
-				XEvent reply = { ClientMessage };
-				reply.xclient.window = xdnd.source;
-				reply.xclient.message_type = XdndFinished;
-				reply.xclient.format = 32;
-				reply.xclient.data.l[0] = (long) win->src.window;
-				reply.xclient.data.l[1] = 0;
-				reply.xclient.data.l[2] = None;
-
-				XSendEvent((Display*) win->src.display, xdnd.source,
-					False, NoEventMask, &reply);
-				XFlush((Display*) win->src.display);
-			}
-
-			RGFW_dndInitCallback(win, win->event.point);
-			break;
-		case SelectionNotify:
-			/* this is only for checking for xdnd drops */
-			if (E.xselection.property != XdndSelection || !(win->src.winArgs | RGFW_ALLOW_DND))
-				break;
-
-			char* data;
-			u64 result;
-
-			Atom actualType;
-			i32 actualFormat;
-			u64 bytesAfter;
-
-			XGetWindowProperty((Display*) win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data);
-
-			if (result == 0)
-				break;
-
-			/*
-			SOURCED FROM GLFW _glfwParseUriList
-			Copyright (c) 2002-2006 Marcus Geelnard
-			Copyright (c) 2006-2019 Camilla Löwy
-			*/
-
-			const char* prefix = "file://";
-
-			char* line;
-
-			win->event.droppedFilesCount = 0;
-
-			win->event.type = RGFW_dnd;
-
-			while ((line = strtok(data, "\r\n"))) {
-				char path[RGFW_MAX_PATH];
-
-				data = NULL;
-
-				if (line[0] == '#')
-					continue;
-
-				char* l;
-				for (l = line; 1; l++) {
-					if ((l - line) > 7)
-						break;
-					else if (*l != prefix[(l - line)])
-						break;
-					else if (*l == '\0' && prefix[(l - line)] == '\0') {
-						line += 7;
-						while (*line != '/')
-							line++;
-						break;
-					} else if (*l == '\0')
-						break;
-				}
-
-				win->event.droppedFilesCount++;
-
-				size_t index = 0;
-				while (*line) {
-					if (line[0] == '%' && line[1] && line[2]) {
-						const char digits[3] = { line[1], line[2], '\0' };
-						path[index] = (char) strtol(digits, NULL, 16);
-						line += 2;
-					} else
-						path[index] = *line;
-
-					index++;
-					line++;
-				}
-				path[index] = '\0';
-				strncpy(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1);
-			}
-
-			if (data)
-				XFree(data);
-
-			if (xdnd.version >= 2) {
-				XEvent reply = { ClientMessage };
-				reply.xclient.window = xdnd.source;
-				reply.xclient.message_type = XdndFinished;
-				reply.xclient.format = 32;
-				reply.xclient.data.l[0] = (long) win->src.window;
-				reply.xclient.data.l[1] = result;
-				reply.xclient.data.l[2] = XdndActionCopy;
-
-				XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply);
-				XFlush((Display*) win->src.display);
-			}
-
-			RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
-			break;
-
-		case FocusIn:
-			win->event.inFocus = 1;
-			win->event.type = RGFW_focusIn;
-			RGFW_focusCallback(win, 1);
-			break;
-
-			break;
-		case FocusOut:
-			win->event.inFocus = 0;
-			win->event.type = RGFW_focusOut;
-			RGFW_focusCallback(win, 0);
-			break;
-		
-		case EnterNotify: {
-			win->event.type = RGFW_mouseEnter;
-			win->event.point.x = E.xcrossing.x;
-			win->event.point.y = E.xcrossing.y;
-			RGFW_mouseNotifyCallBack(win, win->event.point, 1);
-			break;
-		}
-
-		case LeaveNotify: {
-			win->event.type = RGFW_mouseLeave;
-			RGFW_mouseNotifyCallBack(win, win->event.point, 0);
-			break;
-		}
-
-		case ConfigureNotify: {
-				/* detect resize */
-      			if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) {
-					win->event.type = RGFW_windowResized;
-					win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height);
-					RGFW_windowResizeCallback(win, win->r);
-					break;
-      			}  
-      
-      			/* detect move */
-      			if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) {
-					win->event.type = RGFW_windowMoved;
-					win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h);
-					RGFW_windowMoveCallback(win, win->r);
-					break;
-				} 
-
-				break;
-		}
-		default: {
-			break;
-		}
-		}
-
-		XFlush((Display*) win->src.display);
-
-		if (win->event.type)
-			return &win->event;
-		else
-			return NULL;
-	}
-
-	void RGFW_window_move(RGFW_window* win, RGFW_vector v) {
-		assert(win != NULL);
-		win->r.x = v.x;
-		win->r.y = v.y;
-
-		XMoveWindow((Display*) win->src.display, (Window) win->src.window, v.x, v.y);
-	}
-
-
-	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-		win->r.w = a.w;
-		win->r.h = a.h;
-
-		XResizeWindow((Display*) win->src.display, (Window) win->src.window, a.w, a.h);
-	}
-
-	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-
-		if (a.w == 0 && a.h == 0)
-			return;
-
-		XSizeHints hints;
-		long flags;
-
-		XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags);
-
-		hints.flags |= PMinSize;
-		
-		hints.min_width = a.w;
-		hints.min_height = a.h;
-
-		XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints);
-	}
-
-	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-
-		if (a.w == 0 && a.h == 0)
-			return;
-
-		XSizeHints hints;
-		long flags;
-
-		XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags);
-
-		hints.flags |= PMaxSize;
-
-		hints.max_width = a.w;
-		hints.max_height = a.h;
-
-		XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints);
-	}
-
-
-	void RGFW_window_minimize(RGFW_window* win) {
-		assert(win != NULL);
-
-		XIconifyWindow(win->src.display, (Window) win->src.window, DefaultScreen(win->src.display));
-		XFlush(win->src.display);
-	}
-
-	void RGFW_window_restore(RGFW_window* win) {
-		assert(win != NULL);
-
-		XMapWindow(win->src.display, (Window) win->src.window);
-		XFlush(win->src.display);
-	}	
-
-	void RGFW_window_setName(RGFW_window* win, char* name) {
-		assert(win != NULL);
-
-		XStoreName((Display*) win->src.display, (Window) win->src.window, name);
-	}
-	
-	void* RGFW_libxshape = NULL;
-
-	#ifndef RGFW_NO_PASSTHROUGH
-	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
-		assert(win != NULL);
-		
-		#if defined(__CYGWIN__)
-			RGFW_libxshape = dlopen("libXext-6.so", RTLD_LAZY | RTLD_LOCAL);
-		#elif defined(__OpenBSD__) || defined(__NetBSD__)
-			RGFW_libxshape = dlopen("libXext.so", RTLD_LAZY | RTLD_LOCAL);
-		#else
-    		RGFW_libxshape = dlopen("libXext.so.6", RTLD_LAZY | RTLD_LOCAL);
-		#endif
-		
-		typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
-		static PFN_XShapeCombineMask XShapeCombineMask;
-		
-		typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);
-		static PFN_XShapeCombineRegion XShapeCombineRegion;
-		
-		if (XShapeCombineMask != NULL)
-			XShapeCombineMask = (PFN_XShapeCombineMask) dlsym(RGFW_libxshape, "XShapeCombineMask");
-
-		if (XShapeCombineRegion != NULL)
-			XShapeCombineRegion = (PFN_XShapeCombineRegion) dlsym(RGFW_libxshape, "XShapeCombineMask");
-
-		if (passthrough) {
-			Region region = XCreateRegion();
-			XShapeCombineRegion(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet);
-			XDestroyRegion(region);
-
-			return;
-		}
-
-		XShapeCombineMask(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet);
-	}
-	#endif
-
-	/*
-		the majority function is sourced from GLFW
-	*/
-
-	void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-
-		i32 longCount = 2 + a.w * a.h;
-
-		u64* X11Icon = (u64*) RGFW_MALLOC(longCount * sizeof(u64));
-		u64* target = X11Icon;
-
-		*target++ = a.w;
-		*target++ = a.h;
-
-		u32 i;
-
-		for (i = 0; i < a.w * a.h; i++) {
-			if (channels == 3)
-				*target++ = ((icon[i * 3 + 0]) << 16) |
-				((icon[i * 3 + 1]) << 8) |
-				((icon[i * 3 + 2]) << 0) |
-				(0xFF << 24);
-
-			else if (channels == 4)
-				*target++ = ((icon[i * 4 + 0]) << 16) |
-				((icon[i * 4 + 1]) << 8) |
-				((icon[i * 4 + 2]) << 0) |
-				((icon[i * 4 + 3]) << 24);
-		}
-
-		static Atom NET_WM_ICON = 0;
-		if (NET_WM_ICON == 0)
-			NET_WM_ICON = XInternAtom((Display*) win->src.display, "_NET_WM_ICON", False);
-
-		XChangeProperty((Display*) win->src.display, (Window) win->src.window,
-			NET_WM_ICON,
-			6, 32,
-			PropModeReplace,
-			(u8*) X11Icon,
-			longCount);
-
-		RGFW_FREE(X11Icon);
-
-		XFlush((Display*) win->src.display);
-	}
-
-	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-
-#ifndef RGFW_NO_X11_CURSOR
-		XcursorImage* native = XcursorImageCreate(a.w, a.h);
-		native->xhot = 0;
-		native->yhot = 0;
-
-		u8* source = (u8*) image;
-		XcursorPixel* target = native->pixels;
-
-		u32 i;
-		for (i = 0; i < a.w * a.h; i++, target++, source += 4) {
-			u8 alpha = 0xFF;
-			if (channels == 4)
-				alpha = source[3];
-
-			*target = (alpha << 24) | (((source[0] * alpha) / 255) << 16) | (((source[1] * alpha) / 255) << 8) | (((source[2] * alpha) / 255) << 0);
-		}
-
-		Cursor cursor = XcursorImageLoadCursor((Display*) win->src.display, native);
-		XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor);
-
-		XFreeCursor((Display*) win->src.display, (Cursor) cursor);
-		XcursorImageDestroy(native);
-#else
-	RGFW_UNUSED(image) RGFW_UNUSED(a.w) RGFW_UNUSED(channels)
-#endif
-	}
-
-	void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector v) {
-		assert(win != NULL);
-
-		XEvent event;
-		XQueryPointer(win->src.display, DefaultRootWindow(win->src.display),
-			&event.xbutton.root, &event.xbutton.window,
-			&event.xbutton.x_root, &event.xbutton.y_root,
-			&event.xbutton.x, &event.xbutton.y,
-			&event.xbutton.state);
-
-		if (event.xbutton.x == v.x && event.xbutton.y == v.y)
-			return;
-
-		XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) v.x - win->r.x, (int) v.y - win->r.y);
-	}
-
-	RGFWDEF void RGFW_window_disableMouse(RGFW_window* win) {
-		RGFW_UNUSED(win);
-	}
-
-	void RGFW_window_setMouseDefault(RGFW_window* win) {
-		RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW);
-	}
-
-	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
-		assert(win != NULL);
-
-		if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u8)))
-			return;
-		
-		mouse = RGFW_mouseIconSrc[mouse];
-
-		Cursor cursor = XCreateFontCursor((Display*) win->src.display, mouse);
-		XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor);
-
-		XFreeCursor((Display*) win->src.display, (Cursor) cursor);
-	}
-
-	void RGFW_window_hide(RGFW_window* win) {
-		XMapWindow(win->src.display, win->src.window);
-	}
-
-	void RGFW_window_show(RGFW_window* win) {
-		XUnmapWindow(win->src.display, win->src.window);
-	}
-
-	/*
-		the majority function is sourced from GLFW
-	*/
-	char* RGFW_readClipboard(size_t* size) {
-		static Atom UTF8 = 0;
-		if (UTF8 == 0)
-			UTF8 = XInternAtom(RGFW_root->src.display, "UTF8_STRING", True);
-
-		XEvent event;
-		int format;
-		unsigned long N, sizeN;
-		char* data, * s = NULL;
-		Atom target;
-		Atom CLIPBOARD = 0, XSEL_DATA = 0;
-
-		if (CLIPBOARD == 0) {
-			CLIPBOARD = XInternAtom(RGFW_root->src.display, "CLIPBOARD", 0);
-			XSEL_DATA = XInternAtom(RGFW_root->src.display, "XSEL_DATA", 0);
-		}
-
-		XConvertSelection(RGFW_root->src.display, CLIPBOARD, UTF8, XSEL_DATA, RGFW_root->src.window, CurrentTime);
-		XSync(RGFW_root->src.display, 0);
-		XNextEvent(RGFW_root->src.display, &event);
-
-		if (event.type != SelectionNotify || event.xselection.selection != CLIPBOARD || event.xselection.property == 0)
-			return NULL;
-
-		XGetWindowProperty(event.xselection.display, event.xselection.requestor,
-			event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target,
-			&format, &sizeN, &N, (unsigned char**) &data);
-
-		if (target == UTF8 || target == XA_STRING) {
-			s = (char*)RGFW_MALLOC(sizeof(char) * sizeN);
-			strncpy(s, data, sizeN);
-			s[sizeN] = '\0';
-			XFree(data);
-		}
-
-		XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
-
-		if (s != NULL && size != NULL)
-			*size = sizeN;
-
-		return s;
-	}
-
-	/*
-		almost all of this function is sourced from GLFW
-	*/
-	void RGFW_writeClipboard(const char* text, u32 textLen) {
-		static Atom CLIPBOARD = 0,
-			UTF8_STRING = 0,
-			SAVE_TARGETS = 0,
-			TARGETS = 0,
-			MULTIPLE = 0,
-			ATOM_PAIR = 0,
-			CLIPBOARD_MANAGER = 0;
-
-		if (CLIPBOARD == 0) {
-			CLIPBOARD = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD", False);
-			UTF8_STRING = XInternAtom((Display*) RGFW_root->src.display, "UTF8_STRING", False);
-			SAVE_TARGETS = XInternAtom((Display*) RGFW_root->src.display, "SAVE_TARGETS", False);
-			TARGETS = XInternAtom((Display*) RGFW_root->src.display, "TARGETS", False);
-			MULTIPLE = XInternAtom((Display*) RGFW_root->src.display, "MULTIPLE", False);
-			ATOM_PAIR = XInternAtom((Display*) RGFW_root->src.display, "ATOM_PAIR", False);
-			CLIPBOARD_MANAGER = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD_MANAGER", False);
-		}
-
-		XSetSelectionOwner((Display*) RGFW_root->src.display, CLIPBOARD, (Window) RGFW_root->src.window, CurrentTime);
-
-		XConvertSelection((Display*) RGFW_root->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, (Window) RGFW_root->src.window, CurrentTime);
-
-		for (;;) {
-			XEvent event;
-
-			XNextEvent((Display*) RGFW_root->src.display, &event);
-			if (event.type != SelectionRequest)
-				return;
-
-			const XSelectionRequestEvent* request = &event.xselectionrequest;
-
-			XEvent reply = { SelectionNotify };
-
-			char* selectionString = NULL;
-			const Atom formats[] = { UTF8_STRING, XA_STRING };
-			const i32 formatCount = sizeof(formats) / sizeof(formats[0]);
-
-			selectionString = (char*) text;
-
-			if (request->target == TARGETS) {
-				const Atom targets[] = { TARGETS,
-										MULTIPLE,
-										UTF8_STRING,
-										XA_STRING };
-
-				XChangeProperty((Display*) RGFW_root->src.display,
-					request->requestor,
-					request->property,
-					4,
-					32,
-					PropModeReplace,
-					(u8*) targets,
-					sizeof(targets) / sizeof(targets[0]));
-
-				reply.xselection.property = request->property;
-			}
-
-			if (request->target == MULTIPLE) {
-
-				Atom* targets;
-
-				Atom actualType;
-				i32 actualFormat;
-				u64 count, bytesAfter;
-
-				XGetWindowProperty((Display*) RGFW_root->src.display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets);
-
-				u64 i;
-				for (i = 0; i < count; i += 2) {
-					i32 j;
-
-					for (j = 0; j < formatCount; j++) {
-						if (targets[i] == formats[j])
-							break;
-					}
-
-					if (j < formatCount)
-					{
-						XChangeProperty((Display*) RGFW_root->src.display,
-							request->requestor,
-							targets[i + 1],
-							targets[i],
-							8,
-							PropModeReplace,
-							(u8*) selectionString,
-							textLen);
-					} else
-						targets[i + 1] = None;
-				}
-
-				XChangeProperty((Display*) RGFW_root->src.display,
-					request->requestor,
-					request->property,
-					ATOM_PAIR,
-					32,
-					PropModeReplace,
-					(u8*) targets,
-					count);
-
-				XFree(targets);
-
-				reply.xselection.property = request->property;
-			}
-
-			reply.xselection.display = request->display;
-			reply.xselection.requestor = request->requestor;
-			reply.xselection.selection = request->selection;
-			reply.xselection.target = request->target;
-			reply.xselection.time = request->time;
-
-			XSendEvent((Display*) RGFW_root->src.display, request->requestor, False, 0, &reply);
-		}
-	}
-
-	u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) {
-		assert(win != NULL);
-
-#ifdef __linux__
-		char file[15];
-		sprintf(file, "/dev/input/js%i", jsNumber);
-
-		return RGFW_registerJoystickF(win, file);
-#endif
-	}
-
-	u16 RGFW_registerJoystickF(RGFW_window* win, char* file) {
-		assert(win != NULL);
-
-#ifdef __linux__
-
-		i32 js = open(file, O_RDONLY);
-
-		if (js && win->src.joystickCount < 4) {
-			win->src.joystickCount++;
-
-			win->src.joysticks[win->src.joystickCount - 1] = open(file, O_RDONLY);
-
-			u8 i;
-			for (i = 0; i < 16; i++)
-				win->src.jsPressed[win->src.joystickCount - 1][i] = 0;
-
-		}
-
-		else {
-#ifdef RGFW_PRINT_ERRORS
-			RGFW_error = 1;
-			fprintf(stderr, "Error RGFW_registerJoystickF : Cannot open file %s\n", file);
-#endif
-		}
-
-		return win->src.joystickCount - 1;
-#endif
-	}
-
-	u8 RGFW_window_isFullscreen(RGFW_window* win) {
-		assert(win != NULL);
-
-		XWindowAttributes windowAttributes;
-		XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes);
-
-		/* check if the window is visable */
-		if (windowAttributes.map_state != IsViewable)
-			return 0;
-
-		/* check if the window covers the full screen */
-		return (windowAttributes.x == 0 && windowAttributes.y == 0 &&
-			windowAttributes.width == XDisplayWidth(win->src.display, DefaultScreen(win->src.display)) &&
-			windowAttributes.height == XDisplayHeight(win->src.display, DefaultScreen(win->src.display)));
-	}
-
-	u8 RGFW_window_isHidden(RGFW_window* win) {
-		assert(win != NULL);
-
-		XWindowAttributes windowAttributes;
-		XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes);
-
-		return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win));
-	}
-
-	u8 RGFW_window_isMinimized(RGFW_window* win) {
-		assert(win != NULL);
-
-		static Atom prop = 0;
-		if (prop == 0)
-			prop = XInternAtom(win->src.display, "WM_STATE", False);
-
-		Atom actual_type;
-		i32 actual_format;
-		u64 nitems, bytes_after;
-		unsigned char* prop_data;
-
-		i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, prop, 0, 2, False,
-			AnyPropertyType, &actual_type, &actual_format,
-			&nitems, &bytes_after, &prop_data);
-
-		if (status == Success && nitems >= 1 && *((int*) prop_data) == IconicState) {
-			XFree(prop_data);
-			return 1;
-		}
-
-		if (prop_data != NULL)
-			XFree(prop_data);
-
-		return 0;
-	}
-
-	u8 RGFW_window_isMaximized(RGFW_window* win) {
-		assert(win != NULL);
-
-		static Atom net_wm_state = 0;
-		static Atom net_wm_state_maximized_horz = 0;
-		static Atom net_wm_state_maximized_vert = 0;
-
-		if (net_wm_state == 0) {
-			net_wm_state = XInternAtom(win->src.display, "_NET_WM_STATE", False);
-			net_wm_state_maximized_vert = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
-			net_wm_state_maximized_horz = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
-		}
-
-		Atom actual_type;
-		i32 actual_format;
-		u64 nitems, bytes_after;
-		unsigned char* prop_data;
-
-		i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, net_wm_state, 0, 1024, False,
-			XA_ATOM, &actual_type, &actual_format,
-			&nitems, &bytes_after, &prop_data);
-
-		if (status != Success) {
-			if (prop_data != NULL)
-				XFree(prop_data);
-
-			return 0;
-		}
-
-		Atom* atoms = (Atom*) prop_data;
-		u64 i;
-		for (i = 0; i < nitems; ++i) {
-			if (atoms[i] == net_wm_state_maximized_horz ||
-				atoms[i] == net_wm_state_maximized_vert) {
-				XFree(prop_data);
-				return 1;
-			}
-		}
-
-		return 0;
-	}
-
-	static void XGetSystemContentScale(Display* display, float* xscale, float* yscale) {
-		float xdpi = 96.f, ydpi = 96.f;
-
-#ifndef RGFW_NO_DPI
-		char* rms = XResourceManagerString(display);
-		XrmDatabase db = NULL;
-
-		if (rms && db)
-			db = XrmGetStringDatabase(rms);
-
-		if (db == 0) {
-			*xscale = xdpi / 96.f;
-			*yscale = ydpi / 96.f;
-			return;
-		}
-
-		XrmValue value;
-		char* type = NULL;
-
-		if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && strncmp(type, "String", 7) == 0)
-			xdpi = ydpi = atof(value.addr);
-		XrmDestroyDatabase(db);
-#endif
-
-		* xscale = xdpi / 96.f;
-		*yscale = ydpi / 96.f;
-	}
-
-	RGFW_monitor RGFW_XCreateMonitor(i32 screen) {
-		RGFW_monitor monitor;
-
-		Display* display = XOpenDisplay(NULL);
-
-		monitor.rect = RGFW_RECT(0, 0, DisplayWidth(display, screen), DisplayHeight(display, screen));
-		monitor.physW = (monitor.rect.w * 25.4f / 96.f);
-		monitor.physH = (monitor.rect.h * 25.4f / 96.f);
-
-		strncpy(monitor.name, DisplayString(display), 128);
-
-		XGetSystemContentScale(display, &monitor.scaleX, &monitor.scaleY);
-
-		XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen));
-
-		XRRCrtcInfo* ci = NULL;
-		int crtc = 0;
-
-		if (sr->ncrtc > crtc) {
-			ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]);
-		}
-
-		if (ci == NULL) {
-			XRRFreeScreenResources(sr);
-			XCloseDisplay(display);
-			return monitor;
-		}
-
-		monitor.rect.x = ci->x;
-		monitor.rect.y = ci->y;
-
-		XRRFreeCrtcInfo(ci);
-		XRRFreeScreenResources(sr);
-
-		XCloseDisplay(display);
-
-		return monitor;
-	}
-
-	RGFW_monitor RGFW_monitors[6];
-	RGFW_monitor* RGFW_getMonitors(void) {
-		size_t i;
-		for (i = 0; i < (size_t)ScreenCount(RGFW_root->src.display) && i < 6; i++)
-			RGFW_monitors[i] = RGFW_XCreateMonitor(i);
-
-		return RGFW_monitors;
-	}
-
-	RGFW_monitor RGFW_getPrimaryMonitor(void) {
-		assert(RGFW_root != NULL);
-
-		i32 primary = -1;
-		Window root = DefaultRootWindow(RGFW_root->src.display);
-		XRRScreenResources* res = XRRGetScreenResources(RGFW_root->src.display, root);
-
-		for (int i = 0; i < res->noutput; i++) {
-			XRROutputInfo* output_info = XRRGetOutputInfo(RGFW_root->src.display, res, res->outputs[i]);
-			if (output_info->connection == RR_Connected && output_info->crtc) {
-				XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(RGFW_root->src.display, res, output_info->crtc);
-				if (crtc_info->mode != None && crtc_info->x == 0 && crtc_info->y == 0) {
-					primary = i;
-					XRRFreeCrtcInfo(crtc_info);
-					XRRFreeOutputInfo(output_info);
-					break;
-				}
-				XRRFreeCrtcInfo(crtc_info);
-			}
-			XRRFreeOutputInfo(output_info);
-		}
-
-		XRRFreeScreenResources(res);
-
-		return RGFW_XCreateMonitor(primary);
-	}
-
-	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
-		return RGFW_XCreateMonitor(DefaultScreen(win->src.display));
-	}
-
-	#ifdef RGFW_OPENGL
-	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
-		assert(win != NULL);
-
-		glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.rSurf);
-	}
-	#endif
-
-
-	void RGFW_window_swapBuffers(RGFW_window* win) {
-		assert(win != NULL);
-
-		RGFW_window_makeCurrent(win);
-
-		/* clear the window*/
-		if (!(win->src.winArgs & RGFW_NO_CPU_RENDER)) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-			#ifdef RGFW_OSMESA
-			RGFW_OSMesa_reorganize();
-			#endif
-			RGFW_area area = RGFW_bufferSize;
-
-#ifndef RGFW_X11_DONT_CONVERT_BGR
-			win->src.bitmap->data = (char*) win->buffer;
-			u32 x, y;
-			for (y = 0; y < (u32)win->r.h; y++) {
-				for (x = 0; x < (u32)win->r.w; x++) {
-					u32 index = (y * 4 * area.w) + x * 4;
-
-					u8 red = win->src.bitmap->data[index];
-					win->src.bitmap->data[index] = win->buffer[index + 2];
-					win->src.bitmap->data[index + 2] = red;
-
-				}
-			}
-#endif	
-			XPutImage(win->src.display, (Window) win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, RGFW_bufferSize.w, RGFW_bufferSize.h);
-#endif
-		}
-
-		if (!(win->src.winArgs & RGFW_NO_GPU_RENDER)) {
-			#ifdef RGFW_EGL
-					eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
-			#elif defined(RGFW_OPENGL)
-					glXSwapBuffers((Display*) win->src.display, (Window) win->src.window);
-			#endif
-		}
-
-		RGFW_window_checkFPS(win);
-	}
-
-	#if !defined(RGFW_EGL)	
-	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
-		assert(win != NULL);
-
-		#if defined(RGFW_OPENGL)	
-		((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))((Display*) win->src.display, (Window) win->src.window, swapInterval);
-		#endif
-
-		win->fpsCap = (swapInterval == 1) ? 0 : swapInterval;
-	}
-	#endif
-
-
-	void RGFW_window_close(RGFW_window* win) {
-		assert(win != NULL);
-#ifdef RGFW_EGL
-		RGFW_closeEGL(win);
-#endif
-
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-		if (win->buffer != NULL) {
-			XDestroyImage((XImage*) win->src.bitmap);
-			XFreeGC(win->src.display, win->src.gc);
-		}
-#endif
-
-		if ((Display*) win->src.display) {
-#ifdef RGFW_OPENGL
-			glXDestroyContext((Display*) win->src.display, win->src.rSurf);
-#endif
-
-			if (win == RGFW_root)
-				RGFW_root = NULL;
-
-			if ((Drawable) win->src.window)
-				XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /* close the window*/
-			
-			XCloseDisplay((Display*) win->src.display); /* kill the display*/
-		}
-
-#ifdef RGFW_ALLOC_DROPFILES
-		{
-			u32 i;
-			for (i = 0; i < RGFW_MAX_DROPS; i++)
-				RGFW_FREE(win->event.droppedFiles[i]);
-
-
-			RGFW_FREE(win->event.droppedFiles);
-		}
-#endif
-
-		RGFW_windowsOpen--;
-#if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR)
-		if (X11Cursorhandle != NULL && RGFW_windowsOpen <= 0) {
-			dlclose(X11Cursorhandle);
-
-			X11Cursorhandle = NULL;
-		}
-#endif
-
-		if (RGFW_libxshape != NULL && RGFW_windowsOpen <= 0) {
-			dlclose(RGFW_libxshape);
-			RGFW_libxshape = NULL;
-		}
-
-		/* set cleared display / window to NULL for error checking */
-		win->src.display = (Display*) 0;
-		win->src.window = (Window) 0;
-
-		u8 i;
-		for (i = 0; i < win->src.joystickCount; i++)
-			close(win->src.joysticks[i]);
-
-		RGFW_FREE(win); /* free collected window data */
-	}
-
-	u64 	RGFW_getTimeNS(void) { 
-		struct timespec ts = { 0 };
-		clock_gettime(1, &ts);
-		unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
-
-		return nanoSeconds;
-	}
-
-	u64 RGFW_getTime(void) {
-		struct timespec ts = { 0 };
-		clock_gettime(1, &ts);
-		unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
-
-		return (double)(nanoSeconds) * 1e-9;
-	}
-/* 
-	End of linux / unix defines
-*/
-
-#endif /* RGFW_X11 */
-
-
-/*
-
-	Start of Windows defines
-
-
-*/
-
-#ifdef RGFW_WINDOWS
-	#include <processthreadsapi.h>
-	#include <wchar.h>
-	#include <locale.h>
-	#include <windowsx.h>
-	#include <shellapi.h>
-	#include <shellscalingapi.h>
-	#include <windows.h>
-	#include <winuser.rh>
-
-	#ifndef RGFW_NO_XINPUT
-	typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*);
-	PFN_XInputGetState XInputGetStateSRC = NULL;
-	#define XInputGetState XInputGetStateSRC
-
-	typedef DWORD (WINAPI * PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE);
-	PFN_XInputGetKeystroke XInputGetKeystrokeSRC = NULL;
-	#define XInputGetKeystroke XInputGetKeystrokeSRC
-
-	static HMODULE RGFW_XInput_dll = NULL;
-	#endif
-
-	u32 RGFW_mouseIconSrc[] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO};
-
-	char* createUTF8FromWideStringWin32(const WCHAR* source);
-
-#define GL_FRONT				0x0404
-#define GL_BACK					0x0405
-#define GL_LEFT					0x0406
-#define GL_RIGHT				0x0407
-
-#if defined(RGFW_OSMESA) && defined(RGFW_LINK_OSMESA)
-
-	typedef void (GLAPIENTRY* PFN_OSMesaDestroyContext)(OSMesaContext);
-	typedef i32(GLAPIENTRY* PFN_OSMesaMakeCurrent)(OSMesaContext, void*, int, int, int);
-	typedef OSMesaContext(GLAPIENTRY* PFN_OSMesaCreateContext)(GLenum, OSMesaContext);
-
-	PFN_OSMesaMakeCurrent OSMesaMakeCurrentSource;
-	PFN_OSMesaCreateContext OSMesaCreateContextSource;
-	PFN_OSMesaDestroyContext OSMesaDestroyContextSource;
-
-#define OSMesaCreateContext OSMesaCreateContextSource
-#define OSMesaMakeCurrent OSMesaMakeCurrentSource
-#define OSMesaDestroyContext OSMesaDestroyContextSource
-#endif
-
-	typedef int (*PFN_wglGetSwapIntervalEXT)(void);
-	PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL;
-#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc
-
-
-
-#if defined(RGFW_DIRECTX)
-	RGFW_directXinfo RGFW_dxInfo;
-
-	RGFW_directXinfo* RGFW_getDirectXInfo(void) { return &RGFW_dxInfo; }
-#endif
-
-	void* RGFWjoystickApi = NULL;
-
-	/* these two wgl functions need to be preloaded */
-	typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);
-	PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
-
-	/* defines for creating ARB attributes */
-#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
-#define WGL_CONTEXT_MAJOR_VERSION_ARB             0x2091
-#define WGL_CONTEXT_MINOR_VERSION_ARB             0x2092
-#define WGL_DRAW_TO_WINDOW_ARB                    0x2001
-#define WGL_ACCELERATION_ARB                      0x2003
-#define WGL_NO_ACCELERATION_ARB 0x2025
-#define WGL_SUPPORT_OPENGL_ARB                    0x2010
-#define WGL_DOUBLE_BUFFER_ARB                     0x2011
-#define WGL_COLOR_BITS_ARB                        0x2014
-#define WGL_RED_BITS_ARB 0x2015
-#define WGL_RED_SHIFT_ARB 0x2016
-#define WGL_GREEN_BITS_ARB 0x2017
-#define WGL_GREEN_SHIFT_ARB 0x2018
-#define WGL_BLUE_BITS_ARB 0x2019
-#define WGL_BLUE_SHIFT_ARB 0x201a
-#define WGL_ALPHA_BITS_ARB 0x201b
-#define WGL_ALPHA_SHIFT_ARB 0x201c
-#define WGL_ACCUM_BITS_ARB 0x201d
-#define WGL_ACCUM_RED_BITS_ARB 0x201e
-#define WGL_ACCUM_GREEN_BITS_ARB 0x201f
-#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
-#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
-#define WGL_DEPTH_BITS_ARB 0x2022
-#define WGL_AUX_BUFFERS_ARB 0x2024
-#define WGL_STEREO_ARB 0x2012
-#define WGL_DEPTH_BITS_ARB                        0x2022
-#define WGL_STENCIL_BITS_ARB 					  0x2023
-#define WGL_FULL_ACCELERATION_ARB                 0x2027
-#define WGL_CONTEXT_FLAGS_ARB                     0x2094
-#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126
-#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
-#define WGL_SAMPLE_BUFFERS_ARB               0x2041
-#define WGL_SAMPLES_ARB 0x2042
-#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
-
-#ifndef RGFW_EGL
-static HMODULE wglinstance = NULL;
-#endif
-
-#ifdef RGFW_WGL_LOAD
-	typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC);
-	typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC);
-	typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR);
-	typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC);
-	typedef HDC(WINAPI* PFN_wglGetCurrentDC)();
-	typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)();
-
-	PFN_wglCreateContext wglCreateContextSRC;
-	PFN_wglDeleteContext wglDeleteContextSRC;
-	PFN_wglGetProcAddress wglGetProcAddressSRC;
-	PFN_wglMakeCurrent wglMakeCurrentSRC;
-	PFN_wglGetCurrentDC wglGetCurrentDCSRC;
-	PFN_wglGetCurrentContext wglGetCurrentContextSRC;
-
-	#define wglCreateContext wglCreateContextSRC
-	#define wglDeleteContext wglDeleteContextSRC
-	#define wglGetProcAddress wglGetProcAddressSRC
-	#define wglMakeCurrent wglMakeCurrentSRC
-
-	#define wglGetCurrentDC wglGetCurrentDCSRC
-	#define wglGetCurrentContext wglGetCurrentContextSRC
-#endif
-
-#ifdef RGFW_OPENGL
-	void* RGFW_getProcAddress(const char* procname) { 
-		void* proc = (void*) wglGetProcAddress(procname);
-		if (proc)
-			return proc;
-
-		return (void*) GetProcAddress(wglinstance, procname); 
-	}
-
-	typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
-	static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
-#endif
-
-	RGFW_window RGFW_eventWindow;
-
-	LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
-		switch (message) {
-		case WM_MOVE:
-			RGFW_eventWindow.r.x = LOWORD(lParam);
-			RGFW_eventWindow.r.y = HIWORD(lParam);
-			RGFW_eventWindow.src.window = hWnd;
-			return DefWindowProcA(hWnd, message, wParam, lParam);
-		case WM_SIZE:
-			RGFW_eventWindow.r.w = LOWORD(lParam);
-			RGFW_eventWindow.r.h = HIWORD(lParam);
-			RGFW_eventWindow.src.window = hWnd;
-			return DefWindowProcA(hWnd, message, wParam, lParam); // Call DefWindowProc after handling
-		default:
-			return DefWindowProcA(hWnd, message, wParam, lParam);
-		}
-	}
-	
-	#ifndef RGFW_NO_DPI
-	static HMODULE RGFW_Shcore_dll = NULL;
-	typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*);
-	PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL;
-	#define GetDpiForMonitor GetDpiForMonitorSRC
-	#endif
-
-	__declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod);
-
-	#ifndef RGFW_NO_XINPUT
-	void RGFW_loadXInput(void) {
-		u32 i;
-		static const char* names[] = { 
-			"xinput1_4.dll",
-			"xinput1_3.dll",
-			"xinput9_1_0.dll",
-			"xinput1_2.dll",
-			"xinput1_1.dll"
-		};
-
-		for (i = 0; i < sizeof(names) / sizeof(const char*);  i++) {
-			RGFW_XInput_dll = LoadLibraryA(names[i]);
-
-			if (RGFW_XInput_dll) {
-				XInputGetStateSRC = (PFN_XInputGetState)(void*)GetProcAddress(RGFW_XInput_dll, "XInputGetState");
-			
-				if (XInputGetStateSRC == NULL)
-					printf("Failed to load XInputGetState");
-			}
-		}
-	}
-	#endif
-
-	RGFWDEF void RGFW_init_buffer(RGFW_window* win);
-	void RGFW_init_buffer(RGFW_window* win) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-	if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
-		RGFW_bufferSize = RGFW_getScreenSize();
-	
-	BITMAPV5HEADER bi = { 0 };
-	ZeroMemory(&bi, sizeof(bi));
-	bi.bV5Size = sizeof(bi);
-	bi.bV5Width = RGFW_bufferSize.w;
-	bi.bV5Height = -((LONG) RGFW_bufferSize.h);
-	bi.bV5Planes = 1;
-	bi.bV5BitCount = 32;
-	bi.bV5Compression = BI_BITFIELDS;
-	bi.bV5BlueMask = 0x00ff0000;
-	bi.bV5GreenMask = 0x0000ff00;
-	bi.bV5RedMask = 0x000000ff;
-	bi.bV5AlphaMask = 0xff000000;
-
-	win->src.bitmap = CreateDIBSection(win->src.hdc,
-		(BITMAPINFO*) &bi,
-		DIB_RGB_COLORS,
-		(void**) &win->buffer,
-		NULL,
-		(DWORD) 0);
-	
-	win->src.hdcMem = CreateCompatibleDC(win->src.hdc);
-
-	#if defined(RGFW_OSMESA)
-	win->src.rSurf = OSMesaCreateContext(OSMESA_RGBA, NULL);
-	OSMesaMakeCurrent(win->src.rSurf, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
-	#endif
-#else
-RGFW_UNUSED(win); /* if buffer rendering is not being used */
-#endif
-	}
-
-	void RGFW_window_setDND(RGFW_window* win, b8 allow) {
-		DragAcceptFiles(win->src.window, allow);
-	}
-
-	void RGFW_clipCursor(RGFW_rect rect) {
-		if (!rect.x && !rect.y && rect.w && !rect.h) {
-			ClipCursor(NULL);
-			return;
-		}
-
-		RECT r = {rect.x, rect.y, rect.x + rect.w, rect.y + rect.h};
-		ClipCursor(&r);
-	}
-
-	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
-		#ifndef RGFW_NO_XINPUT
-		if (RGFW_XInput_dll == NULL)
-			RGFW_loadXInput();
-		#endif
-
-		#ifndef RGFW_NO_DPI
-		if (RGFW_Shcore_dll == NULL) {
-			RGFW_Shcore_dll = LoadLibraryA("shcore.dll");
-			GetDpiForMonitorSRC = (PFN_GetDpiForMonitor)(void*)GetProcAddress(RGFW_Shcore_dll, "GetDpiForMonitor");
-		}
-		#endif
-
-		if (wglinstance == NULL) {
-			wglinstance = LoadLibraryA("opengl32.dll");
-#ifdef RGFW_WGL_LOAD
-			wglCreateContextSRC = (PFN_wglCreateContext) GetProcAddress(wglinstance, "wglCreateContext");
-			wglDeleteContextSRC = (PFN_wglDeleteContext) GetProcAddress(wglinstance, "wglDeleteContext");
-			wglGetProcAddressSRC = (PFN_wglGetProcAddress) GetProcAddress(wglinstance, "wglGetProcAddress");
-			wglMakeCurrentSRC = (PFN_wglMakeCurrent) GetProcAddress(wglinstance, "wglMakeCurrent");
-			wglGetCurrentDCSRC = (PFN_wglGetCurrentDC) GetProcAddress(wglinstance, "wglGetCurrentDC");
-			wglGetCurrentContextSRC = (PFN_wglGetCurrentContext) GetProcAddress(wglinstance, "wglGetCurrentContext");
-#endif
-		}
-
-		timeBeginPeriod(1);
-
-		if (name[0] == 0) name = (char*) " ";
-
-		RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1);
-		RGFW_eventWindow.src.window = NULL;
-
-		RGFW_window* win = RGFW_window_basic_init(rect, args);
-
-		win->src.maxSize = RGFW_AREA(0, 0);
-		win->src.minSize = RGFW_AREA(0, 0);
-
-
-		HINSTANCE inh = GetModuleHandleA(NULL);
-
-		WNDCLASSA Class = { 0 }; /* Setup the Window class. */
-		Class.lpszClassName = name;
-		Class.hInstance = inh;
-		Class.hCursor = LoadCursor(NULL, IDC_ARROW);
-		Class.lpfnWndProc = WndProc;
-
-		RegisterClassA(&Class);
-
-		DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
-
-		RECT windowRect, clientRect;
-
-		if (!(args & RGFW_NO_BORDER)) {
-			window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_VISIBLE | WS_MINIMIZEBOX;
-
-			if (!(args & RGFW_NO_RESIZE))
-				window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME;
-		} else
-			window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX;
-
-		HWND dummyWin = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0);
-
-		GetWindowRect(dummyWin, &windowRect);
-		GetClientRect(dummyWin, &clientRect);
-
-		win->src.hOffset = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
-		win->src.window = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + win->src.hOffset, 0, 0, inh, 0);
-
-		if (args & RGFW_ALLOW_DND) {
-			win->src.winArgs |= RGFW_ALLOW_DND;
-			RGFW_window_setDND(win, 1);
-		}
-		win->src.hdc = GetDC(win->src.window);
-
-		if ((args & RGFW_NO_INIT_API) == 0) {
-#ifdef RGFW_DIRECTX
-		assert(FAILED(CreateDXGIFactory(&__uuidof(IDXGIFactory), (void**) &RGFW_dxInfo.pFactory)) == 0);
-
-		if (FAILED(RGFW_dxInfo.pFactory->lpVtbl->EnumAdapters(RGFW_dxInfo.pFactory, 0, &RGFW_dxInfo.pAdapter))) {
-			fprintf(stderr, "Failed to enumerate DXGI adapters\n");
-			RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory);
-			return NULL;
-		}
-
-		D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 };
-
-		if (FAILED(D3D11CreateDevice(RGFW_dxInfo.pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &RGFW_dxInfo.pDevice, NULL, &RGFW_dxInfo.pDeviceContext))) {
-			fprintf(stderr, "Failed to create Direct3D device\n");
-			RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter);
-			RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory);
-			return NULL;
-		}
-
-		DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };
-		swapChainDesc.BufferCount = 1;
-		swapChainDesc.BufferDesc.Width = win->r.w;
-		swapChainDesc.BufferDesc.Height = win->r.h;
-		swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
-		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
-		swapChainDesc.OutputWindow = win->src.window;
-		swapChainDesc.SampleDesc.Count = 1;
-		swapChainDesc.SampleDesc.Quality = 0;
-		swapChainDesc.Windowed = TRUE;
-		RGFW_dxInfo.pFactory->lpVtbl->CreateSwapChain(RGFW_dxInfo.pFactory, (IUnknown*) RGFW_dxInfo.pDevice, &swapChainDesc, &win->src.swapchain);
-
-		ID3D11Texture2D* pBackBuffer;
-		win->src.swapchain->lpVtbl->GetBuffer(win->src.swapchain, 0, &__uuidof(ID3D11Texture2D), (LPVOID*) &pBackBuffer);
-		RGFW_dxInfo.pDevice->lpVtbl->CreateRenderTargetView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pBackBuffer, NULL, &win->src.renderTargetView);
-		pBackBuffer->lpVtbl->Release(pBackBuffer);
-
-		D3D11_TEXTURE2D_DESC depthStencilDesc = { 0 };
-		depthStencilDesc.Width = win->r.w;
-		depthStencilDesc.Height = win->r.h;
-		depthStencilDesc.MipLevels = 1;
-		depthStencilDesc.ArraySize = 1;
-		depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
-		depthStencilDesc.SampleDesc.Count = 1;
-		depthStencilDesc.SampleDesc.Quality = 0;
-		depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
-		depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
-
-		ID3D11Texture2D* pDepthStencilTexture = NULL;
-		RGFW_dxInfo.pDevice->lpVtbl->CreateTexture2D(RGFW_dxInfo.pDevice, &depthStencilDesc, NULL, &pDepthStencilTexture);
-
-		D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc = { 0 };
-		depthStencilViewDesc.Format = depthStencilDesc.Format;
-		depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
-		depthStencilViewDesc.Texture2D.MipSlice = 0;
-
-		RGFW_dxInfo.pDevice->lpVtbl->CreateDepthStencilView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pDepthStencilTexture, &depthStencilViewDesc, &win->src.pDepthStencilView);
-
-		pDepthStencilTexture->lpVtbl->Release(pDepthStencilTexture);
-
-		RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, win->src.pDepthStencilView);
-#endif
-
-#ifdef RGFW_OPENGL 
-		HDC dummy_dc = GetDC(dummyWin);
-
-		PIXELFORMATDESCRIPTOR pfd = {
-			.nSize = sizeof(pfd),
-			.nVersion = 1,
-			.iPixelType = PFD_TYPE_RGBA,
-			.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
-			.cColorBits = 24,
-			.cAlphaBits = 8,
-			.iLayerType = PFD_MAIN_PLANE,
-			.cDepthBits = 32,
-			.cStencilBits = 8,
-		};
-
-		int pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
-		SetPixelFormat(dummy_dc, pixel_format, &pfd);
-
-		HGLRC dummy_context = wglCreateContext(dummy_dc);
-		wglMakeCurrent(dummy_dc, dummy_context);
-
-		if (wglChoosePixelFormatARB == NULL) {
-			wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB");
-			wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB");
-		}
-
-		wglMakeCurrent(dummy_dc, 0);
-		wglDeleteContext(dummy_context);
-		ReleaseDC(dummyWin, dummy_dc);
-
-		if (wglCreateContextAttribsARB != NULL) {
-			PIXELFORMATDESCRIPTOR pfd = (PIXELFORMATDESCRIPTOR){ sizeof(pfd), 1, PFD_TYPE_RGBA, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
-			if (args & RGFW_OPENGL_SOFTWARE)
-				pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED;
-
-			if (wglChoosePixelFormatARB != NULL) {
-				i32* pixel_format_attribs = (i32*)RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE);
-
-				int pixel_format;
-				UINT num_formats;
-				wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats);
-				if (!num_formats) {
-					printf("Failed to set the OpenGL 3.3 pixel format.\n");
-				}
-
-				DescribePixelFormat(win->src.hdc, pixel_format, sizeof(pfd), &pfd);
-				if (!SetPixelFormat(win->src.hdc, pixel_format, &pfd)) {
-					printf("Failed to set the OpenGL 3.3 pixel format.\n");
-				}
-			}
-
-			u32 index = 0;
-			i32 attribs[40];
-
-			SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
-
-			if (RGFW_majorVersion || RGFW_minorVersion) {
-				SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion);
-				SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion);
-			}
-
-			SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
-
-			if (RGFW_majorVersion || RGFW_minorVersion) {
-				SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion);
-				SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion);
-			}
-
-			SET_ATTRIB(0, 0);
-
-			win->src.rSurf = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs);
-		} else {
-			fprintf(stderr, "Failed to create an accelerated OpenGL Context\n");
-
-			int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd);
-			SetPixelFormat(win->src.hdc, pixel_format, &pfd);
-
-			win->src.rSurf = wglCreateContext(win->src.hdc);
-		}
-		
-		wglMakeCurrent(win->src.hdc, win->src.rSurf);
-#endif
-	}
-
-#ifdef RGFW_OSMESA
-#ifdef RGFW_LINK_OSM ESA
-		OSMesaMakeCurrentSource = (PFN_OSMesaMakeCurrent) GetProcAddress(win->src.hdc, "OSMesaMakeCurrent");
-		OSMesaCreateContextSource = (PFN_OSMesaCreateContext) GetProcAddress(win->src.hdc, "OSMesaCreateContext");
-		OSMesaDestroyContextSource = (PFN_OSMesaDestroyContext) GetProcAddress(win->src.hdc, "OSMesaDestroyContext");
-#endif
-#endif
-
-#ifdef RGFW_OPENGL
-		if ((args & RGFW_NO_INIT_API) == 0) {
-			ReleaseDC(win->src.window, win->src.hdc);
-			win->src.hdc = GetDC(win->src.window);
-			wglMakeCurrent(win->src.hdc, win->src.rSurf);
-		}
-#endif
-
-		DestroyWindow(dummyWin);
-		RGFW_init_buffer(win);
-
-
-		#ifndef RGFW_NO_MONITOR
-		if (args & RGFW_SCALE_TO_MONITOR)
-			RGFW_window_scaleToMonitor(win);
-		#endif
-
-#ifdef RGFW_EGL
-		if ((args & RGFW_NO_INIT_API) == 0)
-			RGFW_createOpenGLContext(win);
-#endif
-
-		if (args & RGFW_HIDE_MOUSE)
-			RGFW_window_showMouse(win, 0);
-
-		if (args & RGFW_TRANSPARENT_WINDOW) {
-			SetWindowLong(win->src.window, GWL_EXSTYLE, GetWindowLong(win->src.window, GWL_EXSTYLE) | WS_EX_LAYERED);
-			SetLayeredWindowAttributes(win->src.window, RGB(255, 255, 255), RGFW_ALPHA, LWA_ALPHA);
-		}
-
-		ShowWindow(win->src.window, SW_SHOWNORMAL);
-		
-		if (RGFW_root == NULL)
-			RGFW_root = win;
-		
-		#ifdef RGFW_OPENGL
-		else 
-			wglShareLists(RGFW_root->src.rSurf, win->src.rSurf);
-		#endif
-
-		return win;
-	}
-
-	void RGFW_window_setBorder(RGFW_window* win, u8 border) {
-		DWORD style = GetWindowLong(win->src.window, GWL_STYLE);
-
-		if (border == 0) {
-			SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW);
-			SetWindowPos(
-				win->src.window, HWND_TOP, 0, 0, 0, 0,
-				SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
-			);
-		}
-		else {
-			SetWindowLong(win->src.window, GWL_STYLE, style | WS_OVERLAPPEDWINDOW);
-			SetWindowPos(
-				win->src.window, HWND_TOP, 0, 0, 0, 0,
-				SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
-			);
-		}
-	}
-
-
-	RGFW_area RGFW_getScreenSize(void) {
-		return RGFW_AREA(GetDeviceCaps(GetDC(NULL), HORZRES), GetDeviceCaps(GetDC(NULL), VERTRES));
-	}
-
-	RGFW_vector RGFW_getGlobalMousePoint(void) {
-		POINT p;
-		GetCursorPos(&p);
-
-		return RGFW_VECTOR(p.x, p.y);
-	}
-
-	RGFW_vector RGFW_window_getMousePoint(RGFW_window* win) {
-		POINT p;
-		GetCursorPos(&p);
-		ScreenToClient(win->src.window, &p);
-
-		return RGFW_VECTOR(p.x, p.y);
-	}
-
-	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-		win->src.minSize = a;
-	}
-
-	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-		win->src.maxSize = a;
-	}
-
-
-	void RGFW_window_minimize(RGFW_window* win) {
-		assert(win != NULL);
-
-		ShowWindow(win->src.window, SW_MINIMIZE);
-	}
-
-	void RGFW_window_restore(RGFW_window* win) {
-		assert(win != NULL);
-
-		ShowWindow(win->src.window, SW_RESTORE);
-	}
-
-
-	u8 RGFW_xinput2RGFW[] = {
-		RGFW_JS_A, /* or PS X button */
-		RGFW_JS_B, /* or PS circle button */
-		RGFW_JS_X, /* or PS square button */
-		RGFW_JS_Y, /* or PS triangle button */
-		RGFW_JS_R1, /* right bumper */
-		RGFW_JS_L1, /* left bump */
-		RGFW_JS_L2, /* left trigger*/
-		RGFW_JS_R2, /* right trigger */
-		0, 0, 0, 0, 0, 0, 0, 0,
-		RGFW_JS_UP, /* dpad up */
-		RGFW_JS_DOWN, /* dpad down*/
-		RGFW_JS_LEFT, /* dpad left */
-		RGFW_JS_RIGHT, /* dpad right */
-		RGFW_JS_START, /* start button */
-		RGFW_JS_SELECT/* select button */
-	};
-
-	static i32 RGFW_checkXInput(RGFW_window* win, RGFW_Event* e) {
-		size_t i;
-		for (i = 0; i < 4; i++) {
-			XINPUT_KEYSTROKE keystroke;
-
-			if (XInputGetKeystroke == NULL)
-				return 0;
-
-			DWORD result = XInputGetKeystroke((DWORD)i, 0, &keystroke);
-
-			if ((keystroke.Flags & XINPUT_KEYSTROKE_REPEAT) == 0 && result != ERROR_EMPTY) {
-				if (result != ERROR_SUCCESS)
-					return 0;
-
-				if (keystroke.VirtualKey > VK_PAD_BACK)
-					continue;
-
-				// RGFW_jsButtonPressed + 1 = RGFW_jsButtonReleased
-				e->type = RGFW_jsButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
-				e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800];
-				win->src.jsPressed[i][e->button] = !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
-
-				return 1;
-			}
-
-			XINPUT_STATE state;
-			if (XInputGetState == NULL ||
-				XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED
-			)
-				return 0;
-#define INPUT_DEADZONE  ( 0.24f * (float)(0x7FFF) )  // Default to 24% of the +/- 32767 range.   This is a reasonable default value but can be altered if needed.
-
-			if ((state.Gamepad.sThumbLX < INPUT_DEADZONE &&
-				state.Gamepad.sThumbLX > -INPUT_DEADZONE) &&
-				(state.Gamepad.sThumbLY < INPUT_DEADZONE &&
-					state.Gamepad.sThumbLY > -INPUT_DEADZONE))
-			{
-				state.Gamepad.sThumbLX = 0;
-				state.Gamepad.sThumbLY = 0;
-			}
-
-			if ((state.Gamepad.sThumbRX < INPUT_DEADZONE &&
-				state.Gamepad.sThumbRX > -INPUT_DEADZONE) &&
-				(state.Gamepad.sThumbRY < INPUT_DEADZONE &&
-					state.Gamepad.sThumbRY > -INPUT_DEADZONE))
-			{
-				state.Gamepad.sThumbRX = 0;
-				state.Gamepad.sThumbRY = 0;
-			}
-
-			e->axisesCount = 2;
-			RGFW_vector axis1 = RGFW_VECTOR(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY);
-			RGFW_vector axis2 = RGFW_VECTOR(state.Gamepad.sThumbRX, state.Gamepad.sThumbRY);
-
-			if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y || axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) {
-				e->type = RGFW_jsAxisMove;
-
-				e->axis[0] = axis1;
-				e->axis[1] = axis2;
-
-				return 1;
-			}
-
-			e->axis[0] = axis1;
-			e->axis[1] = axis2;
-		}
-
-		return 0;
-	}
-
-	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
-		assert(win != NULL);
-
-		if (win->event.type == RGFW_quit) {
-			return NULL;
-		}
-
-		MSG msg;
-
-		if (RGFW_eventWindow.src.window == win->src.window) {
-			if (RGFW_eventWindow.r.x != -1) {
-				win->r.x = RGFW_eventWindow.r.x;
-				win->r.y = RGFW_eventWindow.r.y;
-				win->event.type = RGFW_windowMoved;
-				RGFW_windowMoveCallback(win, win->r);
-			}
-
-			if (RGFW_eventWindow.r.w != -1) {
-				win->r.w = RGFW_eventWindow.r.w;
-				win->r.h = RGFW_eventWindow.r.h;
-				win->event.type = RGFW_windowResized;
-				RGFW_windowResizeCallback(win, win->r);
-			}
-
-			RGFW_eventWindow.src.window = NULL;
-			RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1);
-
-			return &win->event;
-		}
-
-
-		static HDROP drop;
-		
-		if (win->event.type == RGFW_dnd_init) {
-			if (win->event.droppedFilesCount) {
-				u32 i;
-				for (i = 0; i < win->event.droppedFilesCount; i++)
-					win->event.droppedFiles[i][0] = '\0';
-			}
-
-			win->event.droppedFilesCount = 0;
-			win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0);
-			//win->event.droppedFiles = (char**)RGFW_CALLOC(win->event.droppedFilesCount, sizeof(char*));
-
-			u32 i;
-			for (i = 0; i < win->event.droppedFilesCount; i++) {
-				const UINT length = DragQueryFileW(drop, i, NULL, 0);
-				WCHAR* buffer = (WCHAR*) RGFW_CALLOC((size_t) length + 1, sizeof(WCHAR));
-
-				DragQueryFileW(drop, i, buffer, length + 1);
-				strncpy(win->event.droppedFiles[i], createUTF8FromWideStringWin32(buffer), RGFW_MAX_PATH);
-				win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0';
-				RGFW_FREE(buffer);
-			}
-
-			DragFinish(drop);
-			RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
-			
-			win->event.type = RGFW_dnd;
-			return &win->event;
-		}
-
-		win->event.inFocus = (GetForegroundWindow() == win->src.window);
-
-		if (RGFW_checkXInput(win, &win->event))
-			return &win->event;
-
-		static BYTE keyboardState[256];
-
-		if (PeekMessageA(&msg, win->src.window, 0u, 0u, PM_REMOVE)) {
-			switch (msg.message) {
-			case WM_CLOSE:
-			case WM_QUIT:
-				RGFW_windowQuitCallback(win);
-				win->event.type = RGFW_quit;
-				break;
-
-			case WM_ACTIVATE:
-				win->event.inFocus = (LOWORD(msg.wParam) == WA_INACTIVE);
-
-				if (win->event.inFocus) {
-					win->event.type = RGFW_focusIn;
-					RGFW_focusCallback(win, 1);
-				}
-				else {
-					win->event.type = RGFW_focusOut;
-					RGFW_focusCallback(win, 0);
-				}
-
-				break;
-			
-			case WM_PAINT:
-				win->event.type = RGFW_windowRefresh;
-				RGFW_windowRefreshCallback(win);
-				break;
-			
-			case WM_MOUSELEAVE:
-				win->event.type = RGFW_mouseLeave;
-				win->src.winArgs |= RGFW_MOUSE_LEFT;
-				RGFW_mouseNotifyCallBack(win, win->event.point, 0);
-				break;
-			
-			case WM_KEYUP: {
-				win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam);
-								
-				RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode);
-
-				static char keyName[16];
-				
-				{
-					GetKeyNameTextA((LONG) msg.lParam, keyName, 16);
-
-					if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) ||
-						((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) {
-						CharLowerBuffA(keyName, 16);
-					}
-				}
-
-				RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001));
-
-				strncpy(win->event.keyName, keyName, 16);
-
-				if (RGFW_isPressed(win, RGFW_ShiftL)) {
-					ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR),
-						keyboardState, (LPWORD) win->event.keyName, 0);
-				}
-
-				win->event.type = RGFW_keyReleased;
-				RGFW_keyboard[win->event.keyCode].current = 0;
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0);
-				break;
-			}
-			case WM_KEYDOWN: {
-				win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam);
-
-				RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode);
-
-				static char keyName[16];
-				
-				{
-					GetKeyNameTextA((LONG) msg.lParam, keyName, 16);
-
-					if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) ||
-						((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) {
-						CharLowerBuffA(keyName, 16);
-					}
-				}
-								
-				RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001));
-
-				strncpy(win->event.keyName, keyName, 16);
-
-				if (RGFW_isPressed(win, RGFW_ShiftL) & 0x8000) {
-					ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR),
-						keyboardState, (LPWORD) win->event.keyName, 0);
-				}
-
-				win->event.type = RGFW_keyPressed;
-				RGFW_keyboard[win->event.keyCode].current = 1;
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1);
-				break;
-			}
-
-			case WM_MOUSEMOVE:
-				win->event.type = RGFW_mousePosChanged;
-
-				win->event.point.x = GET_X_LPARAM(msg.lParam);
-				win->event.point.y = GET_Y_LPARAM(msg.lParam);
-
-				RGFW_mousePosCallback(win, win->event.point);
-
-				if (win->src.winArgs & RGFW_MOUSE_LEFT) {
-					win->src.winArgs ^= RGFW_MOUSE_LEFT;
-					win->event.type = RGFW_mouseEnter;
-					RGFW_mouseNotifyCallBack(win, win->event.point, 1);
-				}
-
-				break;
-
-			case WM_LBUTTONDOWN:
-				win->event.button = RGFW_mouseLeft;
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 1;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-			case WM_RBUTTONDOWN:
-				win->event.button = RGFW_mouseRight;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-			case WM_MBUTTONDOWN:
-				win->event.button = RGFW_mouseMiddle;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case WM_MOUSEWHEEL:
-				if (msg.wParam > 0)
-					win->event.button = RGFW_mouseScrollUp;
-				else
-					win->event.button = RGFW_mouseScrollDown;
-
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 1;
-
-				win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA;
-
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case WM_LBUTTONUP:
-			
-				win->event.button = RGFW_mouseLeft;
-				win->event.type = RGFW_mouseButtonReleased;
-
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 0;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-			case WM_RBUTTONUP:
-				win->event.button = RGFW_mouseRight;
-				win->event.type = RGFW_mouseButtonReleased;
-
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 0;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-			case WM_MBUTTONUP:
-				win->event.button = RGFW_mouseMiddle;
-				win->event.type = RGFW_mouseButtonReleased;
-
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 0;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-
-				/*
-					much of this event is source from glfw
-				*/
-			case WM_DROPFILES: {				
-				win->event.type = RGFW_dnd_init;
-
-				drop = (HDROP) msg.wParam;
-				POINT pt;
-
-				/* Move the mouse to the position of the drop */
-				DragQueryPoint(drop, &pt);
-
-				win->event.point.x = pt.x;
-				win->event.point.y = pt.y;
-
-				RGFW_dndInitCallback(win, win->event.point);
-			}
-				break;
-			case WM_GETMINMAXINFO:
-			{
-				if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0)
-					break;
-
-				MINMAXINFO* mmi = (MINMAXINFO*) msg.lParam;
-				mmi->ptMinTrackSize.x = win->src.minSize.w;
-				mmi->ptMinTrackSize.y = win->src.minSize.h;
-				mmi->ptMaxTrackSize.x = win->src.maxSize.w;
-				mmi->ptMaxTrackSize.y = win->src.maxSize.h;
-				return 0;
-			}
-			default:
-				win->event.type = 0;
-				break;
-			}
-
-			TranslateMessage(&msg);
-			DispatchMessageA(&msg);
-		}
-
-		else
-			win->event.type = 0;
-
-		if (!IsWindow(win->src.window)) {
-			win->event.type = RGFW_quit;
-			RGFW_windowQuitCallback(win);
-		}
-
-		if (win->event.type)
-			return &win->event;
-		else
-			return NULL;
-	}
-
-	u8 RGFW_window_isFullscreen(RGFW_window* win) {
-		assert(win != NULL);
-
-		WINDOWPLACEMENT placement = { 0 };
-		GetWindowPlacement(win->src.window, &placement);
-		return placement.showCmd == SW_SHOWMAXIMIZED;
-	}
-
-	u8 RGFW_window_isHidden(RGFW_window* win) {
-		assert(win != NULL);
-
-		return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win);
-	}
-
-	u8 RGFW_window_isMinimized(RGFW_window* win) {
-		assert(win != NULL);
-
-		WINDOWPLACEMENT placement = { 0 };
-		GetWindowPlacement(win->src.window, &placement);
-		return placement.showCmd == SW_SHOWMINIMIZED;
-	}
-
-	u8 RGFW_window_isMaximized(RGFW_window* win) {
-		assert(win != NULL);
-
-		WINDOWPLACEMENT placement = { 0 };
-		GetWindowPlacement(win->src.window, &placement);
-		return placement.showCmd == SW_SHOWMAXIMIZED;
-	}
-
-	typedef struct { int iIndex; HMONITOR hMonitor; } RGFW_mInfo;
-	BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-		RGFW_UNUSED(hdcMonitor)
-		RGFW_UNUSED(lprcMonitor)
-
-		RGFW_mInfo* info = (RGFW_mInfo*) dwData;
-		if (info->hMonitor == hMonitor)
-			return FALSE;
-
-		info->iIndex++;
-		return TRUE;
-	}
-	
-	#ifndef RGFW_NO_MONITOR
-	RGFW_monitor win32CreateMonitor(HMONITOR src) {
-		RGFW_monitor monitor;
-		MONITORINFO monitorInfo;
-
-		monitorInfo.cbSize = sizeof(MONITORINFO);
-		GetMonitorInfoA(src, &monitorInfo);
-
-		RGFW_mInfo info;
-		info.iIndex = 0;
-		info.hMonitor = src;
-
-		/* get the monitor's index */
-		if (EnumDisplayMonitors(NULL, NULL, GetMonitorByHandle, (LPARAM) &info)) {
-			DISPLAY_DEVICEA dd;
-			dd.cb = sizeof(dd);
-
-			/* loop through the devices until you find a device with the monitor's index */
-			size_t deviceIndex;
-			for (deviceIndex = 0; EnumDisplayDevicesA(0, (DWORD) deviceIndex, &dd, 0); deviceIndex++) {
-				char* deviceName = dd.DeviceName;
-				if (EnumDisplayDevicesA(deviceName, info.iIndex, &dd, 0)) {
-					strncpy(monitor.name, dd.DeviceString, 128); /* copy the monitor's name */
-					break;
-				}
-			}
-		}
-
-		monitor.rect.x = monitorInfo.rcWork.left;
-		monitor.rect.y = monitorInfo.rcWork.top;
-		monitor.rect.w = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
-		monitor.rect.h = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
-
-#ifndef RGFW_NO_DPI
-		if (GetDpiForMonitor != NULL) {
-			u32 x, y;
-			GetDpiForMonitor(src, MDT_ANGULAR_DPI, &x, &y);
-			monitor.scaleX = (float) (x) / (float) USER_DEFAULT_SCREEN_DPI;
-			monitor.scaleY = (float) (y) / (float) USER_DEFAULT_SCREEN_DPI;
-		}
-#endif
-
-		HDC hdc = GetDC(NULL);
-		/* get pixels per inch */
-		i32 ppiX = GetDeviceCaps(hdc, LOGPIXELSX);
-		i32 ppiY = GetDeviceCaps(hdc, LOGPIXELSY);
-		ReleaseDC(NULL, hdc);
-
-		/* Calculate physical height in inches */
-		monitor.physW = GetSystemMetrics(SM_CYSCREEN) / (float) ppiX;
-		monitor.physH = GetSystemMetrics(SM_CXSCREEN) / (float) ppiY;
-
-		return monitor;
-	}
-	#endif /* RGFW_NO_MONITOR */
-	
-
-	#ifndef RGFW_NO_MONITOR
-	RGFW_monitor RGFW_monitors[6];
-	BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-		RGFW_UNUSED(hdcMonitor)
-		RGFW_UNUSED(lprcMonitor)
-
-		RGFW_mInfo* info = (RGFW_mInfo*) dwData;
-
-		if (info->iIndex >= 6)
-			return FALSE;
-
-		RGFW_monitors[info->iIndex] = win32CreateMonitor(hMonitor);
-		info->iIndex++;
-
-		return TRUE;
-	}
-
-	RGFW_monitor RGFW_getPrimaryMonitor(void) {
-		return win32CreateMonitor(MonitorFromPoint((POINT) { 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
-	}
-
-	RGFW_monitor* RGFW_getMonitors(void) {
-		RGFW_mInfo info;
-		info.iIndex = 0;
-		while (EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info));
-
-		return RGFW_monitors;
-	}
-
-	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
-		HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY);
-		return win32CreateMonitor(src);
-	}
-	#endif
-
-	HICON RGFW_loadHandleImage(RGFW_window* win, u8* src, RGFW_area a, BOOL icon) {
-		assert(win != NULL);
-
-		u32 i;
-		HDC dc;
-		HICON handle;
-		HBITMAP color, mask;
-		BITMAPV5HEADER bi;
-		ICONINFO ii;
-		u8* target = NULL;
-		u8* source = src;
-
-		ZeroMemory(&bi, sizeof(bi));
-		bi.bV5Size = sizeof(bi);
-		bi.bV5Width = a.w;
-		bi.bV5Height = -((LONG) a.h);
-		bi.bV5Planes = 1;
-		bi.bV5BitCount = 32;
-		bi.bV5Compression = BI_BITFIELDS;
-		bi.bV5RedMask = 0x00ff0000;
-		bi.bV5GreenMask = 0x0000ff00;
-		bi.bV5BlueMask = 0x000000ff;
-		bi.bV5AlphaMask = 0xff000000;
-
-		dc = GetDC(NULL);
-		color = CreateDIBSection(dc,
-			(BITMAPINFO*) &bi,
-			DIB_RGB_COLORS,
-			(void**) &target,
-			NULL,
-			(DWORD) 0);
-		ReleaseDC(NULL, dc);
-
-		mask = CreateBitmap(a.w, a.h, 1, 1, NULL);
-
-		for (i = 0; i < a.w * a.h; i++) {
-			target[0] = source[2];
-			target[1] = source[1];
-			target[2] = source[0];
-			target[3] = source[3];
-			target += 4;
-			source += 4;
-		}
-
-		ZeroMemory(&ii, sizeof(ii));
-		ii.fIcon = icon;
-		ii.xHotspot = 0;
-		ii.yHotspot = 0;
-		ii.hbmMask = mask;
-		ii.hbmColor = color;
-
-		handle = CreateIconIndirect(&ii);
-
-		DeleteObject(color);
-		DeleteObject(mask);
-
-		return handle;
-	}
-
-	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-		RGFW_UNUSED(channels)
-
-		HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(win, image, a, FALSE);
-		SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) cursor);
-		SetCursor(cursor);
-		DestroyCursor(cursor);
-	}
-
-	void RGFW_window_setMouseDefault(RGFW_window* win) {
-		RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW);
-	}
-
-	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
-		assert(win != NULL);
-
-		if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u32)))
-			return;
-
-		char* icon = MAKEINTRESOURCEA(RGFW_mouseIconSrc[mouse]);
-
-		SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon));
-		SetCursor(LoadCursorA(NULL, icon));
-	}
-
-	void RGFW_window_hide(RGFW_window* win) {
-		ShowWindow(win->src.window, SW_HIDE);
-	}
-
-	void RGFW_window_show(RGFW_window* win) {
-		ShowWindow(win->src.window, SW_RESTORE);
-	}
-
-	void RGFW_window_close(RGFW_window* win) {
-		assert(win != NULL);
-
-#ifdef RGFW_EGL
-		RGFW_closeEGL(win);
-#endif
-
-		if (win == RGFW_root) {
-#ifdef RGFW_DIRECTX
-			RGFW_dxInfo.pDeviceContext->lpVtbl->Release(RGFW_dxInfo.pDeviceContext);
-			RGFW_dxInfo.pDevice->lpVtbl->Release(RGFW_dxInfo.pDevice);
-			RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter);
-			RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory);
-#endif
-		
-			if (RGFW_XInput_dll != NULL) {
-				FreeLibrary(RGFW_XInput_dll);
-				RGFW_XInput_dll = NULL;
-			}
-
-			#ifndef RGFW_NO_DPI
-			if (RGFW_Shcore_dll != NULL) {
-				FreeLibrary(RGFW_Shcore_dll);
-				RGFW_Shcore_dll = NULL;
-			}
-			#endif
-
-			if (wglinstance != NULL) {
-				FreeLibrary(wglinstance);
-				wglinstance = NULL;
-			}
-
-			RGFW_root = NULL;
-		}
-
-#ifdef RGFW_DIRECTX
-		win->src.swapchain->lpVtbl->Release(win->src.swapchain);
-		win->src.renderTargetView->lpVtbl->Release(win->src.renderTargetView);
-		win->src.pDepthStencilView->lpVtbl->Release(win->src.pDepthStencilView);
-#endif
-
-#ifdef RGFW_BUFFER
-		DeleteDC(win->src.hdcMem);
-		DeleteObject(win->src.bitmap);
-#endif
-
-#ifdef RGFW_OPENGL
-		wglDeleteContext((HGLRC) win->src.rSurf); /* delete opengl context */
-#endif
-		DeleteDC(win->src.hdc); /* delete device context */
-		DestroyWindow(win->src.window); /* delete window */
-
-#if defined(RGFW_OSMESA)
-		if (win->buffer != NULL)
-			RGFW_FREE(win->buffer);
-#endif
-
-#ifdef RGFW_ALLOC_DROPFILES
-		{
-			u32 i;
-			for (i = 0; i < RGFW_MAX_DROPS; i++)
-				RGFW_FREE(win->event.droppedFiles[i]);
-
-
-			RGFW_FREE(win->event.droppedFiles);
-		}
-#endif
-
-		RGFW_FREE(win);
-	}
-
-	void RGFW_window_move(RGFW_window* win, RGFW_vector v) {
-		assert(win != NULL);
-
-		win->r.x = v.x;
-		win->r.y = v.y;
-		SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, 0, 0, SWP_NOSIZE);
-	}
-
-	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-
-		win->r.w = a.w;
-		win->r.h = a.h;
-		SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w, win->r.h + win->src.hOffset, SWP_NOMOVE);
-	}
-
-
-	void RGFW_window_setName(RGFW_window* win, char* name) {
-		assert(win != NULL);
-
-		SetWindowTextA(win->src.window, name);
-	}
-
-	/* sourced from GLFW */
-	#ifndef RGFW_NO_PASSTHROUGH
-	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
-		assert(win != NULL);
-		
-		COLORREF key = 0;
-		BYTE alpha = 0;
-		DWORD flags = 0;
-		DWORD exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE);
-		
-		if (exStyle & WS_EX_LAYERED)
-			GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags);
-
-		if (passthrough)
-			exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
-		else
-		{
-			exStyle &= ~WS_EX_TRANSPARENT;
-			// NOTE: Window opacity also needs the layered window style so do not
-			//       remove it if the window is alpha blended
-			if (exStyle & WS_EX_LAYERED)
-			{
-				if (!(flags & LWA_ALPHA))
-					exStyle &= ~WS_EX_LAYERED;
-			}
-		}
-
-		SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle);
-
-		if (passthrough) {
-			SetLayeredWindowAttributes(win->src.window, key, alpha, flags);
-		}
-	}
-	#endif
-
-	/* much of this function is sourced from GLFW */
-	void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-		#ifndef RGFW_WIN95
-		RGFW_UNUSED(channels)
-
-		HICON handle = RGFW_loadHandleImage(win, src, a, TRUE);
-
-		SetClassLongPtrA(win->src.window, GCLP_HICON, (LPARAM) handle);
-
-		DestroyIcon(handle);
-		#else
-		RGFW_UNUSED(src)
-		RGFW_UNUSED(a)
-		RGFW_UNUSED(channels)
-		#endif
-	}
-
-	char* RGFW_readClipboard(size_t* size) {
-		/* Open the clipboard */
-		if (OpenClipboard(NULL) == 0)
-			return (char*) "";
-
-		/* Get the clipboard data as a Unicode string */
-		HANDLE hData = GetClipboardData(CF_UNICODETEXT);
-		if (hData == NULL) {
-			CloseClipboard();
-			return (char*) "";
-		}
-
-		wchar_t* wstr = (wchar_t*) GlobalLock(hData);
-
-		char* text;
-
-		{
-			setlocale(LC_ALL, "en_US.UTF-8");
-
-			size_t textLen = wcstombs(NULL, wstr, 0);
-			if (textLen == 0)
-				return (char*) "";
-
-			text = (char*) RGFW_MALLOC((textLen * sizeof(char)) + 1);
-
-			wcstombs(text, wstr, (textLen) +1);
-
-			if (size != NULL)
-				*size = textLen + 1;
-
-			text[textLen] = '\0';
-		}
-
-		/* Release the clipboard data */
-		GlobalUnlock(hData);
-		CloseClipboard();
-
-		return text;
-	}
-
-	void RGFW_writeClipboard(const char* text, u32 textLen) {
-		HANDLE object;
-		WCHAR* buffer;
-
-		object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR));
-		if (!object)
-			return;
-
-		buffer = (WCHAR*) GlobalLock(object);
-		if (!buffer) {
-			GlobalFree(object);
-			return;
-		}
-
-		MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, textLen);
-		GlobalUnlock(object);
-
-		if (!OpenClipboard(RGFW_root->src.window)) {
-			GlobalFree(object);
-			return;
-		}
-
-		EmptyClipboard();
-		SetClipboardData(CF_UNICODETEXT, object);
-		CloseClipboard();
-	}
-
-	u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) {
-		assert(win != NULL);
-
-		RGFW_UNUSED(jsNumber)
-
-		return RGFW_registerJoystickF(win, (char*) "");
-	}
-
-	u16 RGFW_registerJoystickF(RGFW_window* win, char* file) {
-		assert(win != NULL);
-		RGFW_UNUSED(file)
-
-		return win->src.joystickCount - 1;
-	}
-
-	void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector p) {
-		assert(win != NULL);
-
-		SetCursorPos(p.x, p.y);
-	}
-
-	#ifdef RGFW_OPENGL
-	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
-		assert(win != NULL);
-		wglMakeCurrent(win->src.hdc, (HGLRC) win->src.rSurf);
-	}
-	#endif
-
-	#ifndef RGFW_EGL
-	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
-		assert(win != NULL);
-		
-		#if defined(RGFW_OPENGL)
-		typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);
-		static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
-		static void* loadSwapFunc = (void*) 1;
-
-		if (loadSwapFunc == NULL) {
-			fprintf(stderr, "wglSwapIntervalEXT not supported\n");
-			win->fpsCap = (swapInterval == 1) ? 0 : swapInterval;
-			return;
-		}
-
-		if (wglSwapIntervalEXT == NULL) {
-			loadSwapFunc = (void*) wglGetProcAddress("wglSwapIntervalEXT");
-			wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) loadSwapFunc;
-		}
-
-		if (wglSwapIntervalEXT(swapInterval) == FALSE)
-			fprintf(stderr, "Failed to set swap interval\n");
-		#endif
-
-		win->fpsCap = (swapInterval == 1) ? 0 : swapInterval;
-
-	}
-	#endif
-
-	void RGFW_window_swapBuffers(RGFW_window* win) {
-		assert(win != NULL);
-
-		RGFW_window_makeCurrent(win);
-
-		/* clear the window*/
-
-		if (!(win->src.winArgs & RGFW_NO_CPU_RENDER)) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-			#ifdef RGFW_OSMESA
-			RGFW_OSMesa_reorganize();
-			#endif
-
-			HGDIOBJ oldbmp = SelectObject(win->src.hdcMem, win->src.bitmap);
-			BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY);
-			SelectObject(win->src.hdcMem, oldbmp);
-#endif
-		}
-
-		if (!(win->src.winArgs & RGFW_NO_GPU_RENDER)) {
-			#ifdef RGFW_EGL
-					eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
-			#elif defined(RGFW_OPENGL)
-					SwapBuffers(win->src.hdc);
-			#endif
-
-			#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX)
-					win->src.swapchain->lpVtbl->Present(win->src.swapchain, 0, 0);
-			#endif
-		}
-
-		RGFW_window_checkFPS(win);
-	}
-
-	char* createUTF8FromWideStringWin32(const WCHAR* source) {
-		char* target;
-		i32 size;
-
-		size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL);
-		if (!size) {
-			return NULL;
-		}
-
-		target = (char*) RGFW_CALLOC(size, 1);
-
-		if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) {
-			RGFW_FREE(target);
-			return NULL;
-		}
-
-		return target;
-	}
-
-	u64 RGFW_getTimeNS(void) {
-		LARGE_INTEGER frequency;
-		QueryPerformanceFrequency(&frequency);
-
-		LARGE_INTEGER counter;
-		QueryPerformanceCounter(&counter);
-
-		return (u64) (counter.QuadPart * 1e9 / frequency.QuadPart);
-	}
-
-	u64 RGFW_getTime(void) {
-		LARGE_INTEGER frequency;
-		QueryPerformanceFrequency(&frequency);
-
-		LARGE_INTEGER counter;
-		QueryPerformanceCounter(&counter);
-		return (u64) (counter.QuadPart / (double) frequency.QuadPart);
-	}
-	
-	void RGFW_sleep(u64 ms) {
-		Sleep(ms);
-	}
-
-#ifndef RGFW_NO_THREADS
-	RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { return CreateThread(NULL, 0, ptr, args, 0, NULL); }
-	void RGFW_cancelThread(RGFW_thread thread) { CloseHandle((HANDLE) thread); }
-	void RGFW_joinThread(RGFW_thread thread) { WaitForSingleObject((HANDLE) thread, INFINITE); }
-	void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { SetThreadPriority((HANDLE) thread, priority); }
-#endif
-#endif /* RGFW_WINDOWS */
-
-/*
-	End of Windows defines
-*/
-
-
-
-/* 
-
-	Start of MacOS defines
-
-
-*/
-
-#if defined(RGFW_MACOS)
-	/*
-		based on silicon.h
-		start of cocoa wrapper
-	*/
-
-#include <CoreVideo/CVDisplayLink.h>
-#include <ApplicationServices/ApplicationServices.h>
-#include <objc/runtime.h>
-#include <objc/message.h>
-#include <mach/mach_time.h>
-
-	typedef CGRect NSRect;
-	typedef CGPoint NSPoint;
-	typedef CGSize NSSize;
-
-	typedef void NSBitmapImageRep;
-	typedef void NSCursor;
-	typedef void NSDraggingInfo;
-	typedef void NSWindow;
-	typedef void NSApplication;
-	typedef void NSScreen;
-	typedef void NSEvent;
-	typedef void NSString;
-	typedef void NSOpenGLContext;
-	typedef void NSPasteboard;
-	typedef void NSColor;
-	typedef void NSArray;
-	typedef void NSImageRep;
-	typedef void NSImage;
-	typedef void NSOpenGLView;
-
-
-	typedef const char* NSPasteboardType;
-	typedef unsigned long NSUInteger;
-	typedef long NSInteger;
-	typedef NSInteger NSModalResponse;
-
-#ifdef __arm64__
-	/* ARM just uses objc_msgSend */
-#define abi_objc_msgSend_stret objc_msgSend
-#define abi_objc_msgSend_fpret objc_msgSend
-#else /* __i386__ */ 
-	/* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */
-#define abi_objc_msgSend_stret objc_msgSend_stret
-#define abi_objc_msgSend_fpret objc_msgSend_fpret
-#endif
-
-#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc"))
-#define objc_msgSend_bool			((BOOL (*)(id, SEL))objc_msgSend)
-#define objc_msgSend_void			((void (*)(id, SEL))objc_msgSend)
-#define objc_msgSend_void_id		((void (*)(id, SEL, id))objc_msgSend)
-#define objc_msgSend_uint			((NSUInteger (*)(id, SEL))objc_msgSend)
-#define objc_msgSend_void_bool		((void (*)(id, SEL, BOOL))objc_msgSend)
-#define objc_msgSend_void_SEL		((void (*)(id, SEL, SEL))objc_msgSend)
-#define objc_msgSend_id				((id (*)(id, SEL))objc_msgSend)
-
-	void NSRelease(id obj) {
-		objc_msgSend_void(obj, sel_registerName("release"));
-	}
-
-	#define release NSRelease
-
-	NSString* NSString_stringWithUTF8String(const char* str) {	
-		return ((id(*)(id, SEL, const char*))objc_msgSend)
-			((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str);
-	}
-
-	const char* NSString_to_char(NSString* str) {
-		return ((const char* (*)(id, SEL)) objc_msgSend) (str, sel_registerName("UTF8String"));
-	}
-
-	void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) {
-		Class selected_class;
-
-		if (strcmp(class_name, "NSView") == 0) {
-			selected_class = objc_getClass("ViewClass");
-		} else if (strcmp(class_name, "NSWindow") == 0) {
-			selected_class = objc_getClass("WindowClass");
-		} else {
-			selected_class = objc_getClass(class_name);
-		}
-
-		class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0);
-	}
-
-	/* Header for the array. */
-	typedef struct siArrayHeader {
-		size_t count;
-		/* TODO(EimaMei): Add a `type_width` later on. */
-	} siArrayHeader;
-
-	/* Gets the header of the siArray. */
-#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1)
-
-	void* si_array_init_reserve(size_t sizeof_element, size_t count) {
-		siArrayHeader* ptr = malloc(sizeof(siArrayHeader) + (sizeof_element * count));
-		void* array = ptr + sizeof(siArrayHeader);
-
-		siArrayHeader* header = SI_ARRAY_HEADER(array);
-		header->count = count;
-
-		return array;
-	}
-
-#define si_array_len(array) (SI_ARRAY_HEADER(array)->count)
-#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", function)
-	/* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/
-#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", function)
-	
-	unsigned char* NSBitmapImageRep_bitmapData(NSBitmapImageRep* imageRep) {
-		return ((unsigned char* (*)(id, SEL))objc_msgSend)
-			(imageRep, sel_registerName("bitmapData"));
-	}
-
-#define NS_ENUM(type, name) type name; enum
-
-	typedef NS_ENUM(NSUInteger, NSBitmapFormat) {
-		NSBitmapFormatAlphaFirst = 1 << 0,       // 0 means is alpha last (RGBA, CMYKA, etc.)
-			NSBitmapFormatAlphaNonpremultiplied = 1 << 1,       // 0 means is premultiplied
-			NSBitmapFormatFloatingPointSamples = 1 << 2,  // 0 is integer
-
-			NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8),
-			NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9),
-			NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10),
-			NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11)
-	};
-
-	NSBitmapImageRep* NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) {
-		void* func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:");
-
-		return (NSBitmapImageRep*) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, const char*, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits);
-	}
-
-	NSColor* NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) {
-		void* nsclass = objc_getClass("NSColor");
-		void* func = sel_registerName("colorWithSRGBRed:green:blue:alpha:");
-		return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)
-			(nsclass, func, red, green, blue, alpha);
-	}
-
-	NSCursor* NSCursor_initWithImage(NSImage* newImage, NSPoint aPoint) {
-		void* func = sel_registerName("initWithImage:hotSpot:");
-		void* nsclass = objc_getClass("NSCursor");
-
-		return (NSCursor*) ((id(*)(id, SEL, id, NSPoint))objc_msgSend)
-			(NSAlloc(nsclass), func, newImage, aPoint);
-	}
-
-	void NSImage_addRepresentation(NSImage* image, NSImageRep* imageRep) {
-		void* func = sel_registerName("addRepresentation:");
-		objc_msgSend_void_id(image, func, imageRep);
-	}
-
-	NSImage* NSImage_initWithSize(NSSize size) {
-		void* func = sel_registerName("initWithSize:");
-		return ((id(*)(id, SEL, NSSize))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSImage")), func, size);
-	}
-#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers))
-	typedef NS_ENUM(NSInteger, NSOpenGLContextParameter) {
-		NSOpenGLContextParameterSwapInterval           NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param.  0 -> Don't sync, 1 -> Sync to vertical retrace     */
-			NSOpenGLContextParameterSurfaceOrder           NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param.  1 -> Above Window (default), -1 -> Below Window    */
-			NSOpenGLContextParameterSurfaceOpacity         NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param.  1-> Surface is opaque (default), 0 -> non-opaque   */
-			NSOpenGLContextParameterSurfaceBackingSize     NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params.  Width/height of surface backing size              */
-			NSOpenGLContextParameterReclaimResources       NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params.                                                    */
-			NSOpenGLContextParameterCurrentRendererID      NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param.   Retrieves the current renderer ID                 */
-			NSOpenGLContextParameterGPUVertexProcessing    NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param.   Currently processing vertices with GPU (get)      */
-			NSOpenGLContextParameterGPUFragmentProcessing  NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param.   Currently processing fragments with GPU (get)     */
-			NSOpenGLContextParameterHasDrawable            NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param.   Boolean returned if drawable is attached          */
-			NSOpenGLContextParameterMPSwapsInFlight        NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param.   Max number of swaps queued by the MP GL engine    */
-
-			NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params.  Set or get the swap rectangle {x, y, w, h} */
-			NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */
-			NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */
-			NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */
-			NSOpenGLContextParameterSurfaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param.   Surface volatile state */
-	};
-
-
-	void NSOpenGLContext_setValues(NSOpenGLContext* context, const int* vals, NSOpenGLContextParameter param) {
-		void* func = sel_registerName("setValues:forParameter:");
-		((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend)
-			(context, func, vals, param);
-	}
-
-	void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) {
-		void* func = sel_registerName("initWithAttributes:");
-		return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), func, attribs);
-	}
-
-	NSOpenGLView* NSOpenGLView_initWithFrame(NSRect frameRect, uint32_t* format) {
-		void* func = sel_registerName("initWithFrame:pixelFormat:");
-		return (NSOpenGLView*) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSOpenGLView")), func, frameRect, format);
-	}
-
-	void NSCursor_performSelector(NSCursor* cursor, void* selector) {
-		void* func = sel_registerName("performSelector:");
-		objc_msgSend_void_SEL(cursor, func, selector);
-	}
-
-	NSPasteboard* NSPasteboard_generalPasteboard(void) {
-		return (NSPasteboard*) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard"));
-	}
-
-	NSString** cstrToNSStringArray(char** strs, size_t len) {
-		static NSString* nstrs[6];
-		size_t i;
-		for (i = 0; i < len; i++)
-			nstrs[i] = NSString_stringWithUTF8String(strs[i]);
-
-		return nstrs;
-	}
-
-	const char* NSPasteboard_stringForType(NSPasteboard* pasteboard, NSPasteboardType dataType) {
-		void* func = sel_registerName("stringForType:");
-		return (const char*) NSString_to_char(((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, func, NSString_stringWithUTF8String(dataType)));
-	}
-
-	NSArray* c_array_to_NSArray(void* array, size_t len) {
-		SEL func = sel_registerName("initWithObjects:count:");
-		void* nsclass = objc_getClass("NSArray");
-		return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend)
-					(NSAlloc(nsclass), func, array, len);
-	}
- 
-	void NSregisterForDraggedTypes(void* view, NSPasteboardType* newTypes, size_t len) {
-		NSString** ntypes = cstrToNSStringArray((char**)newTypes, len);
-
-		NSArray* array = c_array_to_NSArray(ntypes, len);
-		objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array);
-		NSRelease(array);
-	}
-
-	NSInteger NSPasteBoard_declareTypes(NSPasteboard* pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) {
-		NSString** ntypes = cstrToNSStringArray((char**)newTypes, len);
-
-		void* func = sel_registerName("declareTypes:owner:");
-
-		NSArray* array = c_array_to_NSArray(ntypes, len);
-
-		NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend)
-			(pasteboard, func, array, owner);
-		NSRelease(array);
-
-		return output;
-	}
-
-	bool NSPasteBoard_setString(NSPasteboard* pasteboard, const char* stringToWrite, NSPasteboardType dataType) {
-		void* func = sel_registerName("setString:forType:");
-		return ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend)
-			(pasteboard, func, NSString_stringWithUTF8String(stringToWrite), NSString_stringWithUTF8String(dataType));
-	}
-
-	void NSRetain(id obj) { objc_msgSend_void(obj, sel_registerName("retain")); }
-
-	typedef enum NSApplicationActivationPolicy {
-		NSApplicationActivationPolicyRegular,
-		NSApplicationActivationPolicyAccessory,
-		NSApplicationActivationPolicyProhibited
-	} NSApplicationActivationPolicy;
-
-	typedef NS_ENUM(u32, NSBackingStoreType) {
-		NSBackingStoreRetained = 0,
-			NSBackingStoreNonretained = 1,
-			NSBackingStoreBuffered = 2
-	};
-
-	typedef NS_ENUM(u32, NSWindowStyleMask) {
-		NSWindowStyleMaskBorderless = 0,
-			NSWindowStyleMaskTitled = 1 << 0,
-			NSWindowStyleMaskClosable = 1 << 1,
-			NSWindowStyleMaskMiniaturizable = 1 << 2,
-			NSWindowStyleMaskResizable = 1 << 3,
-			NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */
-			NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12,
-			NSWindowStyleMaskFullScreen = 1 << 14,
-			NSWindowStyleMaskFullSizeContentView = 1 << 15,
-			NSWindowStyleMaskUtilityWindow = 1 << 4,
-			NSWindowStyleMaskDocModalWindow = 1 << 6,
-			NSWindowStyleMaskNonactivatingPanel = 1 << 7,
-			NSWindowStyleMaskHUDWindow = 1 << 13
-	};
-
-	typedef const char* NSPasteboardType;
-	NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPboardType
-
-
-
-	typedef NS_ENUM(i32, NSDragOperation) {
-		NSDragOperationNone = 0,
-			NSDragOperationCopy = 1,
-			NSDragOperationLink = 2,
-			NSDragOperationGeneric = 4,
-			NSDragOperationPrivate = 8,
-			NSDragOperationMove = 16,
-			NSDragOperationDelete = 32,
-			NSDragOperationEvery = ULONG_MAX,
-
-			//NSDragOperationAll_Obsolete	API_DEPRECATED("", macos(10.0,10.10)) = 15, // Use NSDragOperationEvery
-			//NSDragOperationAll API_DEPRECATED("", macos(10.0,10.10)) = NSDragOperationAll_Obsolete, // Use NSDragOperationEvery
-	};
-
-	void* NSArray_objectAtIndex(NSArray* array, NSUInteger index) {
-		void* func = sel_registerName("objectAtIndex:");
-		return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index);
-	}
-
-	const char** NSPasteboard_readObjectsForClasses(NSPasteboard* pasteboard, Class* classArray, size_t len, void* options) {
-		void* func = sel_registerName("readObjectsForClasses:options:");
-
-		NSArray* array = c_array_to_NSArray(classArray, len);
-
-		NSArray* output = (NSArray*) ((id(*)(id, SEL, id, void*))objc_msgSend)
-			(pasteboard, func, array, options);
-
-		NSRelease(array);
-		NSUInteger count = ((NSUInteger(*)(id, SEL))objc_msgSend)(output, sel_registerName("count"));
-
-		const char** res = si_array_init_reserve(sizeof(const char*), count);
-
-		void* path_func = sel_registerName("path");
-
-		for (NSUInteger i = 0; i < count; i++) {
-			void* url = NSArray_objectAtIndex(output, i);
-			NSString* url_str = ((id(*)(id, SEL))objc_msgSend)(url, path_func);
-			res[i] = NSString_to_char(url_str);
-		}
-
-		return res;
-	}
-
-	void* NSWindow_contentView(NSWindow* window) {
-		void* func = sel_registerName("contentView");
-		return objc_msgSend_id(window, func);
-	}
-
-	/*
-		End of cocoa wrapper
-	*/
-
-	char* RGFW_mouseIconSrc[] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"};
-
-	void* RGFWnsglFramework = NULL;
-
-#ifdef RGFW_OPENGL
-	void* RGFW_getProcAddress(const char* procname) {
-		if (RGFWnsglFramework == NULL)
-			RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
-
-		CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII);
-
-		void* symbol = CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName);
-
-		CFRelease(symbolName);
-
-		return symbol;
-	}
-#endif
-
-	CVReturn displayCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { 
-		RGFW_UNUSED(displayLink) RGFW_UNUSED(inNow) RGFW_UNUSED(inOutputTime) RGFW_UNUSED(flagsIn) RGFW_UNUSED(flagsOut) RGFW_UNUSED(displayLinkContext)
-		return kCVReturnSuccess; 
-	}
-
-	id NSWindow_delegate(RGFW_window* win) {
-		return (id) objc_msgSend_id(win->src.window, sel_registerName("delegate"));
-	}
-
-	u32 RGFW_OnClose(void* self) {
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return true;
-
-		win->event.type = RGFW_quit;
-		RGFW_windowQuitCallback(win);
-
-		return true;
-	}
-
-	/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */
-	bool acceptsFirstResponder(void) { return true; }
-	bool performKeyEquivalent(NSEvent* event) { RGFW_UNUSED(event); return true; }
-
-	NSDragOperation draggingEntered(id self, SEL sel, id sender) { 
-		RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);  
-
-		printf("hi\n");
-		return NSDragOperationCopy; 
-	}
-	NSDragOperation draggingUpdated(id self, SEL sel, id sender) { 
-		RGFW_UNUSED(sel); 
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return true;
-		
-		if (!(win->src.winArgs & RGFW_ALLOW_DND)) {
-			return false;
-		}
-
-		win->event.type = RGFW_dnd_init;
-		win->src.dndPassed = 0;
-
-		NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
-
-		win->event.point = RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y));
-		RGFW_dndInitCallback(win, win->event.point);
-
-		return NSDragOperationCopy; 
-	}
-	bool prepareForDragOperation(id self) {
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return true;
-		
-		if (!(win->src.winArgs & RGFW_ALLOW_DND)) {
-			return false;
-		}
-
-		return true;
-	}
-
-	void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);  return; }
-
-	/* NOTE(EimaMei): Usually, you never need 'id self, SEL cmd' for C -> Obj-C methods. This isn't the case. */
-	bool performDragOperation(id self, SEL sel, id sender) {
-		RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); 
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return true;
-
-		//NSWindow* window = objc_msgSend_id(sender, sel_registerName("draggingDestinationWindow"));
-		u32 i;
-		bool found = 0;
-
-		if (!found)
-			i = 0;
-
-		Class array[] = { objc_getClass("NSURL"), NULL };
-		NSPasteboard* pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard"));
-		
-		char** droppedFiles = (char**) NSPasteboard_readObjectsForClasses(pasteBoard, array, 1, NULL);
-
-		win->event.droppedFilesCount = si_array_len(droppedFiles);
-
-		u32 y;
-
-		for (y = 0; y < win->event.droppedFilesCount; y++) {
-			strncpy(win->event.droppedFiles[y], droppedFiles[y], RGFW_MAX_PATH);
-
-			win->event.droppedFiles[y][RGFW_MAX_PATH - 1] = '\0';
-		}
-
-		win->event.type = RGFW_dnd;
-		win->src.dndPassed = 0;
-
-		NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
-		win->event.point = RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y));
-
-		RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
-		return true;
-	}
-
-
-	NSApplication* NSApp = NULL;
-
-	static void NSMoveToResourceDir(void) {
-		/* sourced from glfw */
-		char resourcesPath[255];
-
-		CFBundleRef bundle = CFBundleGetMainBundle();
-		if (!bundle)
-			return;
-
-		CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
-		CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);
-
-		if (
-			CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo ||
-			CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0
-			) {
-			CFRelease(last);
-			CFRelease(resourcesURL);
-			return;
-		}
-
-		CFRelease(last);
-		CFRelease(resourcesURL);
-
-		chdir(resourcesPath);
-	}
-
-
-	NSSize RGFW__osxWindowResize(void* self, SEL sel, NSSize frameSize) {
-		RGFW_UNUSED(sel); 
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return frameSize;
-		
-		win->r.w = frameSize.width;
-		win->r.h = frameSize.height;
-		win->event.type = RGFW_windowResized;
-		RGFW_windowResizeCallback(win, win->r);
-		return frameSize;
-	}
-
-	void RGFW__osxWindowMove(void* self, SEL sel) {
-		RGFW_UNUSED(sel); 
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return;
-		
-		NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)(win->src.window, sel_registerName("frame"));
-		win->r.x = (i32) frame.origin.x;
-		win->r.y = (i32) frame.origin.y;
-
-		win->event.type = RGFW_windowMoved;
-		RGFW_windowMoveCallback(win, win->r);
-	}
-
-	void RGFW__osxUpdateLayer(void* self, SEL sel) {
-		RGFW_UNUSED(sel);
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return;
-		
-		win->event.type = RGFW_windowRefresh;
-		RGFW_windowRefreshCallback(win);
-	}
-
-	RGFWDEF void RGFW_init_buffer(RGFW_window* win);
-	void RGFW_init_buffer(RGFW_window* win) {
-		#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-			if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
-				RGFW_bufferSize = RGFW_getScreenSize();
-				
-			win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4);
-
-		#ifdef RGFW_OSMESA
-				win->src.rSurf = OSMesaCreateContext(OSMESA_RGBA, NULL);
-				OSMesaMakeCurrent(win->src.rSurf, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
-		#endif
-		#else
-		RGFW_UNUSED(win); /* if buffer rendering is not being used */
-		#endif
-	}
-
-	NSPasteboardType const NSPasteboardTypeURL = "public.url";
-	NSPasteboardType const NSPasteboardTypeFileURL  = "public.file-url";
-
-	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
-		static u8 RGFW_loaded = 0;
-
-		/* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea???
-		Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance).
-		*/
-		si_func_to_SEL_with_name("NSObject", "windowShouldClose", RGFW_OnClose);
-
-		/* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */
-		si_func_to_SEL("NSWindow", acceptsFirstResponder);
-		si_func_to_SEL("NSWindow", performKeyEquivalent);
-
-		if (NSApp == NULL) {
-			NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
-
-			((void (*)(id, SEL, NSUInteger))objc_msgSend)
-				(NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
-		}
-
-		RGFW_window* win = RGFW_window_basic_init(rect, args);
-		
-		RGFW_window_setMouseDefault(win);
-
-		NSRect windowRect;
-		windowRect.origin.x = win->r.x;
-		windowRect.origin.y = win->r.y;
-		windowRect.size.width = win->r.w;
-		windowRect.size.height = win->r.h;
-
-		NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled;
-
-		if (!(args & RGFW_NO_RESIZE))
-			macArgs |= NSWindowStyleMaskResizable;
-		if (!(args & RGFW_NO_BORDER))
-			macArgs |= NSWindowStyleMaskTitled;
-		else
-			macArgs = NSWindowStyleMaskBorderless;
-		{
-			void* nsclass = objc_getClass("NSWindow");
-			void* func = sel_registerName("initWithContentRect:styleMask:backing:defer:");
-
-			win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend)
-				(NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false);
-		}
-
-		NSString* str = NSString_stringWithUTF8String(name);
-		objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str);
-
-#ifdef RGFW_OPENGL
-	if ((args & RGFW_NO_INIT_API) == 0) {
-		void* attrs = RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE);
-		void* format = NSOpenGLPixelFormat_initWithAttributes(attrs);
-
-		if (format == NULL) {
-			printf("Failed to load pixel format ");
-
-			void* attrs = RGFW_initAttribs(1);
-			format = NSOpenGLPixelFormat_initWithAttributes(attrs);
-			if (format == NULL)
-				printf("and loading software rendering OpenGL failed\n");
-			else
-				printf("Switching to software rendering\n");
-		}
-
-		win->src.view = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {win->r.w, win->r.h}}, format);
-		objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL"));
-		win->src.rSurf = objc_msgSend_id(win->src.view, sel_registerName("openGLContext"));
-	} else
-#endif
-	{
-		NSRect contentRect = (NSRect){{0, 0}, {win->r.w, win->r.h}};
-		win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSView")), sel_registerName("initWithFrame:"),
-				contentRect);
-	}
-
-		void* contentView = NSWindow_contentView(win->src.window);
-		objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true);
-
-		objc_msgSend_void_id(win->src.window, sel_registerName("setContentView:"), win->src.view);
-
-#ifdef RGFW_OPENGL
-		if ((args & RGFW_NO_INIT_API) == 0)
-			objc_msgSend_void(win->src.rSurf, sel_registerName("makeCurrentContext"));
-#endif
-		if (args & RGFW_TRANSPARENT_WINDOW) {
-#ifdef RGFW_OPENGL
-		if ((args & RGFW_NO_INIT_API) == 0) {
-			i32 opacity = 0;
-			#define NSOpenGLCPSurfaceOpacity 236
-			NSOpenGLContext_setValues(win->src.rSurf, &opacity, NSOpenGLCPSurfaceOpacity);
-		}
-#endif
-
-			objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false);
-
-			objc_msgSend_void_id(win->src.window, sel_registerName("setBackgroundColor:"),
-				NSColor_colorWithSRGB(0, 0, 0, 0));
-		}
-
-		win->src.display = CGMainDisplayID();
-		CVDisplayLinkCreateWithCGDisplay(win->src.display, (CVDisplayLinkRef*)&win->src.displayLink);
-		CVDisplayLinkSetOutputCallback(win->src.displayLink, displayCallback, win);
-		CVDisplayLinkStart(win->src.displayLink);
-
-		RGFW_init_buffer(win);
-
-		#ifndef RGFW_NO_MONITOR
-		if (args & RGFW_SCALE_TO_MONITOR)
-			RGFW_window_scaleToMonitor(win);
-		#endif
-
-		if (args & RGFW_HIDE_MOUSE)
-			RGFW_window_showMouse(win, 0);
-
-		if (args & RGFW_COCOA_MOVE_TO_RESOURCE_DIR)
-			NSMoveToResourceDir();
-
-		Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);
-
-		class_addIvar(
-			delegateClass, "RGFW_window",
-			sizeof(RGFW_window*), rint(log2(sizeof(RGFW_window*))),
-			"L"
-		);
-
-		class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) RGFW__osxWindowResize, "{NSSize=ff}@:{NSSize=ff}");
-		class_addMethod(delegateClass, sel_registerName("updateLayer:"), (IMP) RGFW__osxUpdateLayer, "");
-		class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, "");
-		class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, "");
-		class_addMethod(delegateClass, sel_registerName("draggingEntered:"), (IMP)draggingEntered, "l@:@");
-		class_addMethod(delegateClass, sel_registerName("draggingUpdated:"), (IMP)draggingUpdated, "l@:@");
-		class_addMethod(delegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
-		class_addMethod(delegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
-		class_addMethod(delegateClass, sel_registerName("prepareForDragOperation:"), (IMP)prepareForDragOperation, "B@:@");
-		class_addMethod(delegateClass, sel_registerName("performDragOperation:"), (IMP)performDragOperation, "B@:@");
-
-		id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init"));
-
-		object_setInstanceVariable(delegate, "RGFW_window", win);
-
-		objc_msgSend_void_id(win->src.window, sel_registerName("setDelegate:"), delegate);
-
-		if (args & RGFW_ALLOW_DND) {
-			win->src.winArgs |= RGFW_ALLOW_DND;
-
-			NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString};
-			NSregisterForDraggedTypes(win->src.window, types, 3);
-		}
-
-		// Show the window
-		((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
-
-		if (!RGFW_loaded) {
-			objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow"));
-
-			RGFW_loaded = 1;
-		}
-
-		objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow"));
-
-		objc_msgSend_void(NSApp, sel_registerName("finishLaunching"));
-
-		if (RGFW_root == NULL)
-			RGFW_root = win;
-
-		NSRetain(win->src.window);
-		NSRetain(NSApp);
-
-		return win;
-	}
-
-	void RGFW_window_setBorder(RGFW_window* win, u8 border) {
-		NSBackingStoreType storeType = NSWindowStyleMaskBorderless;
-		if (!border) {
-			storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
-		}
-		if (!(win->src.winArgs & RGFW_NO_RESIZE)) {
-			storeType |= NSWindowStyleMaskResizable;
-		}
-		
-		((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)(win->src.window, sel_registerName("setStyleMask:"), storeType);
-
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setHasShadow:"), border);
-	}
-
-	RGFW_area RGFW_getScreenSize(void) {
-		static CGDirectDisplayID display = 0;
-
-		if (display == 0)
-			display = CGMainDisplayID();
-
-		return RGFW_AREA(CGDisplayPixelsWide(display), CGDisplayPixelsHigh(display));
-	}
-
-	RGFW_vector RGFW_getGlobalMousePoint(void) {
-		assert(RGFW_root != NULL);
-
-		CGEventRef e = CGEventCreate(NULL);
-		CGPoint point = CGEventGetLocation(e);
-		CFRelease(e);
-
-		return RGFW_VECTOR((u32) point.x, (u32) point.y); /* the point is loaded during event checks */
-	}
-
-	RGFW_vector RGFW_window_getMousePoint(RGFW_window* win) {
-		NSPoint p =  ((NSPoint(*)(id, SEL)) objc_msgSend)(win->src.window, sel_registerName("mouseLocationOutsideOfEventStream"));
-
-		return RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y));
-	}
-
-	u32 RGFW_keysPressed[10]; /*10 keys at a time*/
-	typedef NS_ENUM(u32, NSEventType) {        /* various types of events */
-		NSEventTypeLeftMouseDown = 1,
-			NSEventTypeLeftMouseUp = 2,
-			NSEventTypeRightMouseDown = 3,
-			NSEventTypeRightMouseUp = 4,
-			NSEventTypeMouseMoved = 5,
-			NSEventTypeLeftMouseDragged = 6,
-			NSEventTypeRightMouseDragged = 7,
-			NSEventTypeMouseEntered = 8,
-			NSEventTypeMouseExited = 9,
-			NSEventTypeKeyDown = 10,
-			NSEventTypeKeyUp = 11,
-			NSEventTypeFlagsChanged = 12,
-			NSEventTypeAppKitDefined = 13,
-			NSEventTypeSystemDefined = 14,
-			NSEventTypeApplicationDefined = 15,
-			NSEventTypePeriodic = 16,
-			NSEventTypeCursorUpdate = 17,
-			NSEventTypeScrollWheel = 22,
-			NSEventTypeTabletPoint = 23,
-			NSEventTypeTabletProximity = 24,
-			NSEventTypeOtherMouseDown = 25,
-			NSEventTypeOtherMouseUp = 26,
-			NSEventTypeOtherMouseDragged = 27,
-			/* The following event types are available on some hardware on 10.5.2 and later */
-			NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29,
-			NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30,
-			NSEventTypeSwipe   API_AVAILABLE(macos(10.5)) = 31,
-			NSEventTypeRotate  API_AVAILABLE(macos(10.5)) = 18,
-			NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19,
-			NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20,
-
-			NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32,
-			NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33,
-
-			NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34,
-			NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37,
-
-			NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38,
-	};
-
-	typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */
-		NSEventMaskLeftMouseDown = 1ULL << NSEventTypeLeftMouseDown,
-			NSEventMaskLeftMouseUp = 1ULL << NSEventTypeLeftMouseUp,
-			NSEventMaskRightMouseDown = 1ULL << NSEventTypeRightMouseDown,
-			NSEventMaskRightMouseUp = 1ULL << NSEventTypeRightMouseUp,
-			NSEventMaskMouseMoved = 1ULL << NSEventTypeMouseMoved,
-			NSEventMaskLeftMouseDragged = 1ULL << NSEventTypeLeftMouseDragged,
-			NSEventMaskRightMouseDragged = 1ULL << NSEventTypeRightMouseDragged,
-			NSEventMaskMouseEntered = 1ULL << NSEventTypeMouseEntered,
-			NSEventMaskMouseExited = 1ULL << NSEventTypeMouseExited,
-			NSEventMaskKeyDown = 1ULL << NSEventTypeKeyDown,
-			NSEventMaskKeyUp = 1ULL << NSEventTypeKeyUp,
-			NSEventMaskFlagsChanged = 1ULL << NSEventTypeFlagsChanged,
-			NSEventMaskAppKitDefined = 1ULL << NSEventTypeAppKitDefined,
-			NSEventMaskSystemDefined = 1ULL << NSEventTypeSystemDefined,
-			NSEventMaskApplicationDefined = 1ULL << NSEventTypeApplicationDefined,
-			NSEventMaskPeriodic = 1ULL << NSEventTypePeriodic,
-			NSEventMaskCursorUpdate = 1ULL << NSEventTypeCursorUpdate,
-			NSEventMaskScrollWheel = 1ULL << NSEventTypeScrollWheel,
-			NSEventMaskTabletPoint = 1ULL << NSEventTypeTabletPoint,
-			NSEventMaskTabletProximity = 1ULL << NSEventTypeTabletProximity,
-			NSEventMaskOtherMouseDown = 1ULL << NSEventTypeOtherMouseDown,
-			NSEventMaskOtherMouseUp = 1ULL << NSEventTypeOtherMouseUp,
-			NSEventMaskOtherMouseDragged = 1ULL << NSEventTypeOtherMouseDragged,
-			/* The following event masks are available on some hardware on 10.5.2 and later */
-			NSEventMaskGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeGesture,
-			NSEventMaskMagnify API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeMagnify,
-			NSEventMaskSwipe API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeSwipe,
-			NSEventMaskRotate API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeRotate,
-			NSEventMaskBeginGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeBeginGesture,
-			NSEventMaskEndGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeEndGesture,
-
-			/* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit.
-			 */
-			NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) = 1ULL << NSEventTypeSmartMagnify,
-			NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) = 1ULL << NSEventTypePressure,
-			NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) = 1ULL << NSEventTypeDirectTouch,
-
-			NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) = 1ULL << NSEventTypeChangeMode,
-
-			NSEventMaskAny = ULONG_MAX,
-
-	};
-
-	typedef enum NSEventModifierFlags {
-		NSEventModifierFlagCapsLock = 1 << 16,
-		NSEventModifierFlagShift = 1 << 17,
-		NSEventModifierFlagControl = 1 << 18,
-		NSEventModifierFlagOption = 1 << 19,
-		NSEventModifierFlagCommand = 1 << 20,
-		NSEventModifierFlagNumericPad = 1 << 21
-	} NSEventModifierFlags;
-
-	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
-		assert(win != NULL);
-		
-		if (win->event.type == RGFW_quit)
-			return NULL;
-
-		if ((win->event.type == RGFW_dnd || win->event.type == RGFW_dnd_init) && win->src.dndPassed == 0) {
-			win->src.dndPassed = 1;
-			return &win->event;
-		}
-
-		static void* eventFunc = NULL;
-		if (eventFunc == NULL)
-			eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
-		
-		if ((win->event.type == RGFW_windowMoved || win->event.type == RGFW_windowResized || win->event.type == RGFW_windowRefresh) && win->event.keyCode != 120) {
-			win->event.keyCode = 120;
-			return &win->event;
-		}
-
-		NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend)
-			(NSApp, eventFunc, ULONG_MAX, NULL, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
-
-		if (e == NULL)
-			return NULL;
-
-		if (objc_msgSend_id(e, sel_registerName("window")) != win->src.window) {
-			((void (*)(id, SEL, id, bool))objc_msgSend)
-				(NSApp, sel_registerName("postEvent:atStart:"), e, 0);
-
-			return NULL;
-		}
-
-		if (win->event.droppedFilesCount) {
-			u32 i;
-			for (i = 0; i < win->event.droppedFilesCount; i++)
-				win->event.droppedFiles[i][0] = '\0';
-		}
-
-		win->event.droppedFilesCount = 0;
-		win->event.type = 0;
-
-		switch (objc_msgSend_uint(e, sel_registerName("type"))) {
-			case NSEventTypeMouseEntered: {
-				win->event.type = RGFW_mouseEnter;
-				NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
-
-				win->event.point = RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y));
-				RGFW_mouseNotifyCallBack(win, win->event.point, 1);
-				break;
-			}
-			
-			case NSEventTypeMouseExited:
-				win->event.type = RGFW_mouseLeave;
-				RGFW_mouseNotifyCallBack(win, win->event.point, 0);
-				break;
-
-			case NSEventTypeKeyDown: {
-				u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
-				win->event.keyCode = RGFW_apiKeyCodeToRGFW(key);
-				RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current;
-
-				win->event.type = RGFW_keyPressed;
-				char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters")));
-				strncpy(win->event.keyName, str, 16);
-				RGFW_keyboard[win->event.keyCode].current = 1;
-
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1);
-				break;
-			}
-
-			case NSEventTypeKeyUp: {
-				u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
-				win->event.keyCode = RGFW_apiKeyCodeToRGFW(key);;
-
-				RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current;
-
-				win->event.type = RGFW_keyReleased;
-				char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters")));
-				strncpy(win->event.keyName, str, 16);
-
-				RGFW_keyboard[win->event.keyCode].current = 0;
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0);
-				break;
-			}
-
-			case NSEventTypeFlagsChanged: {
-				u32 flags = objc_msgSend_uint(e, sel_registerName("modifierFlags"));
-				RGFW_updateLockState(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255));
-				
-				u8 i;
-				for (i = 0; i < 9; i++)
-					RGFW_keyboard[i + RGFW_CapsLock].prev = 0;
-				
-				for (i = 0; i < 5; i++) {
-					u32 shift = (1 << (i + 16));
-					u32 key = i + RGFW_CapsLock;
-
-					if ((flags & shift) && !RGFW_wasPressed(win, key)) {
-						RGFW_keyboard[key].current = 1;
-
-						if (key != RGFW_CapsLock)
-							RGFW_keyboard[key+ 4].current = 1;
-						
-						win->event.type = RGFW_keyPressed;
-						win->event.keyCode = key;
-						break;
-					} 
-					
-					if (!(flags & shift) && RGFW_wasPressed(win, key)) {
-						RGFW_keyboard[key].current = 0;
-						
-						if (key != RGFW_CapsLock)
-							RGFW_keyboard[key + 4].current = 0;
-
-						win->event.type = RGFW_keyReleased;
-						win->event.keyCode = key;
-						break;
-					}
-				}
-
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, win->event.type == RGFW_keyPressed);
-
-				break;
-			}
-			case NSEventTypeLeftMouseDragged:
-			case NSEventTypeOtherMouseDragged:
-			case NSEventTypeRightMouseDragged:
-			case NSEventTypeMouseMoved:
-				win->event.type = RGFW_mousePosChanged;
-				NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
-				win->event.point = RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y));
-
-				if ((win->src.winArgs & RGFW_HOLD_MOUSE)) {
-					p.x = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaX"));
-					p.y = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY"));
-					
-					p.x = ((win->r.w / 2)) + p.x;
-					p.y = ((win->r.h / 2)) + p.y;
-					win->event.point = RGFW_VECTOR((u32) p.x, (u32) (p.y));
-				}
-
-				RGFW_mousePosCallback(win, win->event.point);
-				break;
-
-			case NSEventTypeLeftMouseDown:
-				win->event.button = RGFW_mouseLeft;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case NSEventTypeOtherMouseDown:
-				win->event.button = RGFW_mouseMiddle;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case NSEventTypeRightMouseDown:
-				win->event.button = RGFW_mouseRight;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case NSEventTypeLeftMouseUp:
-				win->event.button = RGFW_mouseLeft;
-				win->event.type = RGFW_mouseButtonReleased;
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 0;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-
-			case NSEventTypeOtherMouseUp:
-				win->event.button = RGFW_mouseMiddle;
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 0;
-				win->event.type = RGFW_mouseButtonReleased;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-
-			case NSEventTypeRightMouseUp:
-				win->event.button = RGFW_mouseRight;
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 0;
-				win->event.type = RGFW_mouseButtonReleased;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-
-			case NSEventTypeScrollWheel: {
-				double deltaY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY"));
-
-				if (deltaY > 0) {
-					win->event.button = RGFW_mouseScrollUp;
-				}
-				else if (deltaY < 0) {
-					win->event.button = RGFW_mouseScrollDown;
-				}
-
-				RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button];
-				RGFW_mouseButtons[win->event.button] = 1;
-
-				win->event.scroll = deltaY;
-
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-			}
-
-			default:
-				break;
-		}
-
-		objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
-
-		return &win->event;
-	}
-
-
-	void RGFW_window_move(RGFW_window* win, RGFW_vector v) {
-		assert(win != NULL);
-
-		win->r.x = v.x;
-		win->r.y = v.y;
-		((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
-			(win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true);
-	}
-
-	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-
-		win->r.w = a.w;
-		win->r.h = a.h;
-		((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
-			(win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true);
-	}
-
-	void RGFW_window_minimize(RGFW_window* win) {
-		assert(win != NULL);
-
-		objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL);
-	}
-
-	void RGFW_window_restore(RGFW_window* win) {
-		assert(win != NULL);
-
-		objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL);
-	}
-
-	void RGFW_window_setName(RGFW_window* win, char* name) {
-		assert(win != NULL);
-
-		NSString* str = NSString_stringWithUTF8String(name);
-		objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str);
-	}
-
-	#ifndef RGFW_NO_PASSTHROUGH
-	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough);
-	}
-	#endif
-
-	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
-		if (a.w == 0 && a.h == 0)
-			return;
-
-		((void (*)(id, SEL, NSSize))objc_msgSend)
-			(win->src.window, sel_registerName("setMinSize:"), (NSSize){a.w, a.h});
-	}
-
-	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
-		if (a.w == 0 && a.h == 0)
-			return;
-
-		((void (*)(id, SEL, NSSize))objc_msgSend)
-			(win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h});
-	}
-
-	void RGFW_window_setIcon(RGFW_window* win, u8* data, RGFW_area area, i32 channels) {
-		assert(win != NULL);
-
-		/* code by EimaMei  */
-		// Make a bitmap representation, then copy the loaded image into it.
-		void* representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * channels, 8 * channels);
-		memcpy(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * channels);
-
-		// Add ze representation.
-		void* dock_image = NSImage_initWithSize((NSSize){area.w, area.h});
-		NSImage_addRepresentation(dock_image, (void*) representation);
-
-		// Finally, set the dock image to it.
-		objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image);
-		// Free the garbage.
-		release(dock_image);
-		release(representation);
-	}
-
-	NSCursor* NSCursor_arrowStr(char* str) {
-		void* nclass = objc_getClass("NSCursor");
-		void* func = sel_registerName(str);
-		return (NSCursor*) objc_msgSend_id(nclass, func);
-	}
-
-	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-
-		if (image == NULL) {
-			objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set"));
-			return;
-		}
-
-		/* NOTE(EimaMei): Code by yours truly. */
-		// Make a bitmap representation, then copy the loaded image into it.
-		void* representation = NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * channels, 8 * channels);
-		memcpy(NSBitmapImageRep_bitmapData(representation), image, a.w * a.h * channels);
-
-		// Add ze representation.
-		void* cursor_image = NSImage_initWithSize((NSSize){a.w, a.h});
-		NSImage_addRepresentation(cursor_image, representation);
-
-		// Finally, set the cursor image.
-		void* cursor = NSCursor_initWithImage(cursor_image, (NSPoint){0.0, 0.0});
-
-		objc_msgSend_void(cursor, sel_registerName("set"));
-
-		// Free the garbage.
-		release(cursor_image);
-		release(representation);
-	}
-
-	void RGFW_window_setMouseDefault(RGFW_window* win) {
-		RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW);
-	}
-
-	void RGFW_window_showMouse(RGFW_window* win, i8 show) {
-		RGFW_UNUSED(win);
-
-		if (show) {
-			CGDisplayShowCursor(kCGDirectMainDisplay);
-		}
-		else {
-			CGDisplayHideCursor(kCGDirectMainDisplay);
-		}
-	}
-
-	void RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouses) {
-		if (stdMouses > ((sizeof(RGFW_mouseIconSrc)) / (sizeof(char*))))
-			return;
-		
-		char* mouseStr = RGFW_mouseIconSrc[stdMouses];
-		void* mouse = NSCursor_arrowStr(mouseStr);
-
-		if (mouse == NULL)
-			return;
-
-		RGFW_UNUSED(win);
-		CGDisplayShowCursor(kCGDirectMainDisplay);
-		objc_msgSend_void(mouse, sel_registerName("set"));
-	}
-
-	void RGFW_clipCursor(RGFW_rect r) { 
-		CGWarpMouseCursorPosition(CGPointMake(r.x + (r.w / 2), r.y + (r.h / 2)));
-		CGAssociateMouseAndMouseCursorPosition((!r.x && !r.y && r.w && !r.h));
-	}
-
-	void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector v) {
-		RGFW_UNUSED(win);
-
-		CGWarpMouseCursorPosition(CGPointMake(v.x, v.y));		
-	}
-
-
-	void RGFW_window_hide(RGFW_window* win) {
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false);
-	}
-
-	void RGFW_window_show(RGFW_window* win) {
-		((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
-	}
-
-	u8 RGFW_window_isFullscreen(RGFW_window* win) {
-		assert(win != NULL);
-
-		NSWindowStyleMask mask = (NSWindowStyleMask) objc_msgSend_uint(win->src.window, sel_registerName("styleMask"));
-		return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
-	}
-
-	u8 RGFW_window_isHidden(RGFW_window* win) {
-		assert(win != NULL);
-
-		bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible"));
-		return visible == NO && !RGFW_window_isMinimized(win);
-	}
-
-	u8 RGFW_window_isMinimized(RGFW_window* win) {
-		assert(win != NULL);
-
-		return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES;
-	}
-
-	u8 RGFW_window_isMaximized(RGFW_window* win) {
-		assert(win != NULL);
-
-		return objc_msgSend_bool(win->src.window, sel_registerName("isZoomed"));
-	}
-
-	static RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display) {
-		RGFW_monitor monitor;
-
-		CGRect bounds = CGDisplayBounds(display);
-		monitor.rect = RGFW_RECT((int) bounds.origin.x, (int) bounds.origin.y, (int) bounds.size.width, (int) bounds.size.height);
-
-		CGSize screenSizeMM = CGDisplayScreenSize(display);
-		monitor.physW = screenSizeMM.width / 25.4;
-		monitor.physH = screenSizeMM.height / 25.4;
-
-		monitor.scaleX = (monitor.rect.w / (screenSizeMM.width)) / 2.6;
-		monitor.scaleY = (monitor.rect.h / (screenSizeMM.height)) / 2.6;
-
-		snprintf(monitor.name, 128, "%i %i %i", CGDisplayModelNumber(display), CGDisplayVendorNumber(display), CGDisplaySerialNumber(display));
-
-		return monitor;
-	}
-
-
-	static RGFW_monitor RGFW_monitors[7];
-
-	RGFW_monitor* RGFW_getMonitors(void) {
-		static CGDirectDisplayID displays[7];
-		u32 count;
-
-		if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess)
-			return NULL;
-
-		for (u32 i = 0; i < count; i++)
-			RGFW_monitors[i] = RGFW_NSCreateMonitor(displays[i]);
-
-		return RGFW_monitors;
-	}
-
-	RGFW_monitor RGFW_getPrimaryMonitor(void) {
-		CGDirectDisplayID primary = CGMainDisplayID();
-		return RGFW_NSCreateMonitor(primary);
-	}
-
-	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
-		return RGFW_NSCreateMonitor(win->src.display);
-	}
-
-	char* RGFW_readClipboard(size_t* size) {
-		char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString);
-		
-		size_t clip_len = 1;
-
-		if (clip != NULL) {
-			clip_len = strlen(clip) + 1; 
-		}
-
-		char* str = (char*)RGFW_MALLOC(sizeof(char) * clip_len);
-		
-		if (clip != NULL) {
-			strncpy(str, clip, clip_len);
-		}
-
-		str[clip_len] = '\0';
-		
-		if (size != NULL)
-			*size = clip_len;
-		return str;
-	}
-
-	void RGFW_writeClipboard(const char* text, u32 textLen) {
-		RGFW_UNUSED(textLen);
-
-		NSPasteboardType array[] = { NSPasteboardTypeString, NULL };
-		NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL);
-
-		NSPasteBoard_setString(NSPasteboard_generalPasteboard(), text, NSPasteboardTypeString);
-	}
-
-	u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) {
-		RGFW_UNUSED(jsNumber);
-
-		assert(win != NULL);
-
-		return RGFW_registerJoystickF(win, (char*) "");
-	}
-
-	u16 RGFW_registerJoystickF(RGFW_window* win, char* file) {
-		RGFW_UNUSED(file);
-
-		assert(win != NULL);
-
-		return win->src.joystickCount - 1;
-	}
-
-	#ifdef RGFW_OPENGL
-	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
-		assert(win != NULL);
-		objc_msgSend_void(win->src.rSurf, sel_registerName("makeCurrentContext"));
-	}
-	#endif
-
-	#if !defined(RGFW_EGL)
-	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
-		assert(win != NULL);
-		#if defined(RGFW_OPENGL)
-		
-		NSOpenGLContext_setValues(win->src.rSurf, &swapInterval, 222);
-		#endif
-
-		win->fpsCap = (swapInterval == 1) ? 0 : swapInterval;
-	}
-	#endif
-	
-	void RGFW_window_swapBuffers(RGFW_window* win) {
-		assert(win != NULL);
-
-		RGFW_window_makeCurrent(win);
-
-		/* clear the window*/
-
-		if (!(win->src.winArgs & RGFW_NO_CPU_RENDER)) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-			#ifdef RGFW_OSMESA
-			RGFW_OSMesa_reorganize();
-			#endif
-
-			RGFW_area area = RGFW_bufferSize;
-			void* view = NSWindow_contentView(win->src.window);
-			void* layer = objc_msgSend_id(view, sel_registerName("layer"));
-
-			((void(*)(id, SEL, NSRect))objc_msgSend)(layer,
-				sel_registerName("setFrame:"),
-				(NSRect){{0, 0}, {win->r.w, win->r.h}});
-
-			NSBitmapImageRep* rep = NSBitmapImageRep_initWithBitmapData(
-				&win->buffer, win->r.w, win->r.h, 8, 4, true, false,
-				"NSDeviceRGBColorSpace", 0,
-				area.w * 4, 32
-			);
-			id image = NSAlloc((id)objc_getClass("NSImage"));
-			NSImage_addRepresentation(image, rep);
-			objc_msgSend_void_id(layer, sel_registerName("setContents:"), (id) image);
-
-			release(image);
-			release(rep);
-#endif
-		}
-
-		if (!(win->src.winArgs & RGFW_NO_GPU_RENDER)) {
-			#ifdef RGFW_EGL
-					eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
-			#elif defined(RGFW_OPENGL)
-					objc_msgSend_void(win->src.rSurf, sel_registerName("flushBuffer"));
-			#endif
-		}
-
-		RGFW_window_checkFPS(win);
-	}
-
-	void RGFW_window_close(RGFW_window* win) {
-		assert(win != NULL);
-		release(win->src.view);
-
-#ifdef RGFW_ALLOC_DROPFILES
-		{
-			u32 i;
-			for (i = 0; i < RGFW_MAX_DROPS; i++)
-				RGFW_FREE(win->event.droppedFiles[i]);
-
-
-			RGFW_FREE(win->event.droppedFiles);
-		}
-#endif
-
-		if (RGFW_root == win) {
-			objc_msgSend_void_id(NSApp, sel_registerName("terminate:"), (id) win->src.window);
-			NSApp = NULL;
-		}
-
-#ifdef RGFW_BUFFER
-		release(win->src.bitmap);
-		release(win->src.image);
-#endif
-
-		CVDisplayLinkStop(win->src.displayLink);
-		CVDisplayLinkRelease(win->src.displayLink);
-
-		RGFW_FREE(win);
-	}
-
-	u64 RGFW_getTimeNS(void) {
-		static mach_timebase_info_data_t timebase_info;
-		if (timebase_info.denom == 0) {
-			mach_timebase_info(&timebase_info);
-		}
-		return mach_absolute_time() * timebase_info.numer / timebase_info.denom;
-	}
-
-	u64 RGFW_getTime(void) {
-		static mach_timebase_info_data_t timebase_info;
-		if (timebase_info.denom == 0) {
-			mach_timebase_info(&timebase_info);
-		}
-		return (double) mach_absolute_time() * (double) timebase_info.numer / ((double) timebase_info.denom * 1e9);
-	}
-#endif /* RGFW_MACOS */
-
-/*
-	End of MaOS defines
-*/
-
-/* unix (macOS, linux) only stuff */
-#if defined(RGFW_X11) || defined(RGFW_MACOS)
-/* unix threading */
-#ifndef RGFW_NO_THREADS
-#include <pthread.h>
-
-	RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) {
-		RGFW_UNUSED(args);
-		
-		RGFW_thread t;
-		pthread_create((pthread_t*) &t, NULL, *ptr, NULL);
-		return t;
-	}
-	void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); }
-	void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); }
-#ifdef __linux__
-	void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio(thread, priority); }
-#endif
-#endif
-/* unix sleep */
-	void RGFW_sleep(u64 ms) {
-		struct timespec time;
-		time.tv_sec = 0;
-		time.tv_nsec = ms * 1e+6;
-
-		nanosleep(&time, NULL);
-	}
-
-#endif /* end of unix / mac stuff*/
-#endif /*RGFW_IMPLEMENTATION*/
-
-#ifdef __cplusplus
+
+ 	#define RGFW_EXPORT - Use when building RGFW 
+    #define RGFW_IMPORT - Use when linking with RGFW (not as a single-header)
+	
+	#define RGFW_STD_INT - force the use stdint.h (for systems that might not have stdint.h (msvc)) 
+*/
+
+/*
+	Credits :
+		EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support, siliapp.h -> referencing 
+
+		stb - This project is heavily inspired by the stb single header files
+
+		GLFW:
+			certain parts of winapi and X11 are very poorly documented,
+			GLFW's source code was referenced and used throughout the project (used code is marked in some way),
+			this mainly includes, code for drag and drops, code for setting the icon to a bitmap and the code for managing the clipboard for X11 (as these parts are not documented very well)
+
+			GLFW Copyright, https::/github.com/GLFW/GLFW
+
+			Copyright (c) 2002-2006 Marcus Geelnard
+			Copyright (c) 2006-2019 Camilla Löwy
+
+		contributors : (feel free to put yourself here if you contribute)
+		krisvers -> code review
+		EimaMei (SaCode) -> code review
+		Code-Nycticebus -> bug fixes
+		Rob Rohan -> X11 bugs and missing features, MacOS/Cocoa fixing memory issues/bugs 
+		AICDG (@THISISAGOODNAME) -> vulkan support (example)
+		@Easymode -> support, testing/debugging, bug fixes and reviews
+*/
+
+#if _MSC_VER
+	#pragma comment(lib, "gdi32")
+	#pragma comment(lib, "shell32")
+	#pragma comment(lib, "opengl32")
+	#pragma comment(lib, "winmm")
+	#pragma comment(lib, "user32")
+#endif
+
+#ifndef RGFW_MALLOC
+	#include <stdlib.h>
+
+	#ifndef __USE_POSIX199309
+	#define __USE_POSIX199309
+	#endif
+
+	#include <time.h>
+	#define RGFW_MALLOC malloc
+	#define RGFW_CALLOC calloc
+	#define RGFW_FREE free
+#endif
+
+#if !_MSC_VER
+	#ifndef inline
+		#ifndef __APPLE__
+			#define inline __inline
+		#endif
+	#endif
+#endif
+
+#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */
+	#define RGFW_NO_MONITOR
+	#define RGFW_NO_PASSTHROUGH
+#endif
+
+#if defined(RGFW_EXPORT) ||  defined(RGFW_IMPORT)
+	#if defined(_WIN32)
+		#if defined(__TINYC__) && (defined(RGFW_EXPORT) ||  defined(RGFW_IMPORT))
+			#define __declspec(x) __attribute__((x))
+		#endif
+
+		#if defined(RGFW_EXPORT)
+			#define RGFWDEF __declspec(dllexport)
+		#else 
+			#define RGFWDEF __declspec(dllimport)
+		#endif
+	#else
+		#if defined(RGFW_EXPORT)
+			#define RGFWDEF __attribute__((visibility("default")))
+		#endif
+	#endif
+#endif 
+
+#ifndef RGFWDEF
+	#ifdef __clang__
+		#define RGFWDEF static inline
+	#else
+		#define RGFWDEF inline
+	#endif
+#endif
+
+#ifndef RGFW_ENUM
+	#define RGFW_ENUM(type, name) type name; enum
+#endif
+
+#ifndef RGFW_UNUSED
+	#define RGFW_UNUSED(x) (void)(x);
+#endif
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
+	extern "C" {
+#endif
+
+	/* makes sure the header file part is only defined once by default */
+#ifndef RGFW_HEADER
+
+#define RGFW_HEADER
+
+#if !defined(u8)
+	#if ((defined(_MSC_VER) || defined(__SYMBIAN32__)) && !defined(RGFW_STD_INT)) /* MSVC might not have stdint.h */
+		typedef unsigned char 	u8;
+		typedef signed char		i8;
+		typedef unsigned short  u16;
+		typedef signed short 	i16;
+		typedef unsigned int 	u32;
+		typedef signed int		i32;
+		typedef unsigned long	u64;
+		typedef signed long		i64;
+	#else /* use stdint standard types instead of c ""standard"" types */
+		#include <stdint.h>
+
+		typedef uint8_t     u8;
+		typedef int8_t      i8;
+		typedef uint16_t   u16;
+		typedef int16_t    i16;
+		typedef uint32_t   u32;
+		typedef int32_t    i32;
+		typedef uint64_t   u64;
+		typedef int64_t    i64;
+	#endif
+#endif
+
+#if !defined(b8) /* RGFW bool type */
+	typedef u8 b8;
+	typedef u32 b32;
+#endif
+
+#define RGFW_TRUE (!(0))
+#define RGFW_FALSE 0
+
+/* thse OS macros looks better & are standardized */
+/* plus it helps with cross-compiling */
+
+#ifdef __EMSCRIPTEN__
+	#define RGFW_WEBASM
+
+	#ifndef RGFW_NO_API
+		#define RGFW_OPENGL
+	#endif
+
+	#ifdef RGFW_EGL
+		#undef RGFW_EGL
+	#endif
+
+	#include <emscripten/html5.h>
+	#include <emscripten/key_codes.h>
+#endif
+
+#if defined(RGFW_X11) && defined(__APPLE__)
+	#define RGFW_MACOS_X11
+	#undef __APPLE__
+#endif
+
+#if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_WEBASM) /* (if you're using X11 on windows some how) */
+	#define RGFW_WINDOWS
+
+	/* make sure the correct architecture is defined */
+	#if defined(_WIN64)
+		#define _AMD64_
+		#undef _X86_
+	#else
+		#undef _AMD64_
+		#ifndef _X86_
+			#define _X86_
+		#endif
+	#endif
+
+	#ifndef RGFW_NO_XINPUT
+		#ifdef __MINGW32__ /* try to find the right header */
+			#include <xinput.h>
+		#else
+			#include <XInput.h>
+		#endif
+	#endif
+
+	#if defined(RGFW_DIRECTX)
+		#include <d3d11.h>
+		#include <dxgi.h>
+		#include <dxgi.h>
+		#include <d3dcompiler.h>
+
+		#ifndef __cplusplus
+			#define __uuidof(T) IID_##T
+		#endif
+	#endif
+
+#elif defined(RGFW_WAYLAND)
+    #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL))
+		#define RGFW_EGL
+		#define RGFW_OPENGL
+		#include <wayland-egl.h>
+	#endif
+
+	#include <wayland-client.h>
+#elif (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11))  && !defined(RGFW_WEBASM)
+	#define RGFW_MACOS_X11
+	#define RGFW_X11
+	#include <X11/Xlib.h>
+#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11)  && !defined(RGFW_WEBASM)
+	#define RGFW_MACOS
+#endif
+
+#if (defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)) && !defined(RGFW_EGL)
+	#define RGFW_EGL
+#endif
+
+#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API)
+	#define RGFW_OPENGL
+#endif
+
+#ifdef RGFW_EGL
+	#include <EGL/egl.h>
+#elif defined(RGFW_OSMESA)
+	#ifndef __APPLE__
+		#include <GL/osmesa.h>
+	#else
+		#include <OpenGL/osmesa.h>
+	#endif
+#endif
+
+#if defined(RGFW_OPENGL) && defined(RGFW_X11)
+	#ifndef GLX_MESA_swap_control
+		#define  GLX_MESA_swap_control
+	#endif
+	#include <GL/glx.h> /* GLX defs, xlib.h, gl.h */
+#endif
+
+#ifndef RGFW_ALPHA
+	#define RGFW_ALPHA 128 /* alpha value for RGFW_TRANSPARENT_WINDOW (WINAPI ONLY, macOS + linux don't need this) */
+#endif
+
+/*! Optional arguments for making a windows */
+#define RGFW_TRANSPARENT_WINDOW		(1L<<9) /*!< the window is transparent (only properly works on X11 and MacOS, although it's although for windows) */
+#define RGFW_NO_BORDER		(1L<<3) /*!< the window doesn't have border */
+#define RGFW_NO_RESIZE		(1L<<4) /*!< the window cannot be resized  by the user */
+#define RGFW_ALLOW_DND     (1L<<5) /*!< the window supports drag and drop*/
+#define RGFW_HIDE_MOUSE (1L<<6) /*! the window should hide the mouse or not (can be toggled later on) using `RGFW_window_mouseShow*/
+#define RGFW_FULLSCREEN (1L<<8) /* the window is fullscreen by default or not */
+#define RGFW_CENTER (1L<<10) /*! center the window on the screen */
+#define RGFW_OPENGL_SOFTWARE (1L<<11) /*! use OpenGL software rendering */
+#define RGFW_COCOA_MOVE_TO_RESOURCE_DIR (1L << 12) /* (cocoa only), move to resource folder */
+#define RGFW_SCALE_TO_MONITOR (1L << 13) /* scale the window to the screen */
+#define RGFW_NO_INIT_API (1L << 2) /* DO not init an API (mostly for bindings, you should use `#define RGFW_NO_API` in C */
+
+#define RGFW_NO_GPU_RENDER (1L<<14) /* don't render (using the GPU based API)*/
+#define RGFW_NO_CPU_RENDER (1L<<15) /* don't render (using the CPU based buffer rendering)*/
+#define RGFW_WINDOW_HIDE (1L <<  16)/* the window is hidden */
+
+typedef RGFW_ENUM(u8, RGFW_event_types) {
+	/*! event codes */
+ 	RGFW_keyPressed = 1, /* a key has been pressed */
+	RGFW_keyReleased, /*!< a key has been released*/
+	/*! key event note
+		the code of the key pressed is stored in
+		RGFW_Event.keyCode
+		!!Keycodes defined at the bottom of the RGFW_HEADER part of this file!!
+
+		while a string version is stored in
+		RGFW_Event.KeyString
+
+		RGFW_Event.lockState holds the current lockState
+		this means if CapsLock, NumLock are active or not
+	*/
+	RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right)*/
+	RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right)*/
+	RGFW_mousePosChanged, /*!< the position of the mouse has been changed*/
+	/*! mouse event note
+		the x and y of the mouse can be found in the vector, RGFW_Event.point
+
+		RGFW_Event.button holds which mouse button was pressed
+	*/
+	RGFW_jsButtonPressed, /*!< a joystick button was pressed */
+	RGFW_jsButtonReleased, /*!< a joystick button was released */
+	RGFW_jsAxisMove, /*!< an axis of a joystick was moved*/
+	/*! joystick event note
+		RGFW_Event.joystick holds which joystick was altered, if any
+		RGFW_Event.button holds which joystick button was pressed
+
+		RGFW_Event.axis holds the data of all the axis
+		RGFW_Event.axisCount says how many axis there are
+	*/
+	RGFW_windowMoved, /*!< the window was moved (by the user) */
+	RGFW_windowResized, /*!< the window was resized (by the user), [on webASM this means the browser was resized] */
+	RGFW_focusIn, /*!< window is in focus now */
+	RGFW_focusOut, /*!< window is out of focus now */
+	RGFW_mouseEnter, /* mouse entered the window */
+	RGFW_mouseLeave, /* mouse left the window */
+	RGFW_windowRefresh, /* The window content needs to be refreshed */
+
+	/* attribs change event note
+		The event data is sent straight to the window structure
+		with win->r.x, win->r.y, win->r.w and win->r.h
+	*/
+	RGFW_quit, /*!< the user clicked the quit button*/ 
+	RGFW_dnd, /*!< a file has been dropped into the window*/
+	RGFW_dnd_init /*!< the start of a dnd event, when the place where the file drop is known */
+	/* dnd data note
+		The x and y coords of the drop are stored in the vector RGFW_Event.point
+
+		RGFW_Event.droppedFilesCount holds how many files were dropped
+
+		This is also the size of the array which stores all the dropped file string,
+		RGFW_Event.droppedFiles
+	*/
+};
+
+/*! mouse button codes (RGFW_Event.button) */
+#define RGFW_mouseLeft  1 /*!< left mouse button is pressed*/
+#define RGFW_mouseMiddle  2 /*!< mouse-wheel-button is pressed*/
+#define RGFW_mouseRight  3 /*!< right mouse button is pressed*/
+#define RGFW_mouseScrollUp  4 /*!< mouse wheel is scrolling up*/
+#define RGFW_mouseScrollDown  5 /*!< mouse wheel is scrolling down*/
+
+#ifndef RGFW_MAX_PATH
+#define RGFW_MAX_PATH 260 /* max length of a path (for dnd) */
+#endif
+#ifndef RGFW_MAX_DROPS
+#define RGFW_MAX_DROPS 260 /* max items you can drop at once */
+#endif
+
+
+/* for RGFW_Event.lockstate */
+#define RGFW_CAPSLOCK (1L << 1)
+#define RGFW_NUMLOCK (1L << 2)
+
+/*! joystick button codes (based on xbox/playstation), you may need to change these values per controller */
+#ifndef RGFW_joystick_codes
+	typedef RGFW_ENUM(u8, RGFW_joystick_codes) {
+		RGFW_JS_A = 0, /*!< or PS X button */
+		RGFW_JS_B = 1, /*!< or PS circle button */
+		RGFW_JS_Y = 2, /*!< or PS triangle button */
+		RGFW_JS_X = 3, /*!< or PS square button */
+		RGFW_JS_START = 9, /*!< start button */
+		RGFW_JS_SELECT = 8, /*!< select button */
+		RGFW_JS_HOME = 10, /*!< home button */
+		RGFW_JS_UP = 13, /*!< dpad up */
+		RGFW_JS_DOWN = 14, /*!< dpad down*/
+		RGFW_JS_LEFT = 15, /*!< dpad left */
+		RGFW_JS_RIGHT = 16, /*!< dpad right */
+		RGFW_JS_L1 = 4, /*!< left bump */
+		RGFW_JS_L2 = 5, /*!< left trigger*/
+		RGFW_JS_R1 = 6, /*!< right bumper */
+		RGFW_JS_R2 = 7, /*!< right trigger */
+	};
+#endif
+
+/*! basic vector type, if there's not already a point/vector type of choice */
+#ifndef RGFW_point
+	typedef struct { i32 x, y; } RGFW_point;
+#endif
+
+/*! basic rect type, if there's not already a rect type of choice */
+#ifndef RGFW_rect
+	typedef struct { i32 x, y, w, h; } RGFW_rect;
+#endif
+
+/*! basic area type, if there's not already a area type of choice */
+#ifndef RGFW_area
+	typedef struct { u32 w, h; } RGFW_area;
+#endif
+
+#ifndef __cplusplus
+#define RGFW_POINT(x, y) (RGFW_point){(i32)(x), (i32)(y)}
+#define RGFW_RECT(x, y, w, h) (RGFW_rect){(i32)(x), (i32)(y), (i32)(w), (i32)(h)}
+#define RGFW_AREA(w, h) (RGFW_area){(u32)(w), (u32)(h)}
+#else
+#define RGFW_POINT(x, y) {(i32)(x), (i32)(y)}
+#define RGFW_RECT(x, y, w, h) {(i32)(x), (i32)(y), (i32)(w), (i32)(h)}
+#define RGFW_AREA(w, h) {(u32)(w), (u32)(h)}
+#endif
+
+#ifndef RGFW_NO_MONITOR
+	/*! structure for monitor data */
+	typedef struct RGFW_monitor {
+		char name[128]; /*!< monitor name */
+		RGFW_rect rect; /*!< monitor Workarea */
+		float scaleX, scaleY; /*!< monitor content scale*/
+		float physW, physH; /*!< monitor physical size */
+	} RGFW_monitor;
+
+	/*
+		NOTE : Monitor functions should be ran only as many times as needed (not in a loop)
+	*/
+
+	/*! get an array of all the monitors (max 6) */
+	RGFWDEF RGFW_monitor* RGFW_getMonitors(void);
+	/*! get the primary monitor */
+	RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void);
+#endif
+
+/* NOTE: some parts of the data can represent different things based on the event (read comments in RGFW_Event struct) */
+/*! Event structure for checking/getting events */
+typedef struct RGFW_Event {
+	char keyName[16]; /*!< key name of event*/
+
+	/*! drag and drop data */
+	/* 260 max paths with a max length of 260 */
+#ifdef RGFW_ALLOC_DROPFILES
+	char** droppedFiles;
+#else
+	char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH]; /*!< dropped files*/
+#endif
+	u32 droppedFilesCount; /*!< house many files were dropped */
+
+	u32 type; /*!< which event has been sent?*/
+	RGFW_point point; /*!< mouse x, y of event (or drop point) */
+	
+	u8 keyCode; /*!< keycode of event 	!!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */	
+	
+	b8 repeat; /*!< key press event repeated (the key is being held) */
+	b8 inFocus;  /*!< if the window is in focus or not (this is always true for MacOS windows due to the api being weird) */
+
+	u8 lockState;
+	
+	u8 button; /* !< which mouse button was pressed */
+	double scroll; /*!< the raw mouse scroll value */
+
+	u16 joystick; /*! which joystick this event applies to (if applicable to any) */
+	u8 axisesCount; /*!< number of axises */
+	RGFW_point axis[2]; /*!< x, y of axises (-100 to 100) */
+
+	u64 frameTime, frameTime2; /*!< this is used for counting the fps */
+} RGFW_Event;
+
+/*! source data for the window (used by the APIs) */
+typedef struct RGFW_window_src {
+#ifdef RGFW_WINDOWS
+	HWND window; /*!< source window */
+	HDC hdc; /*!< source HDC */
+	u32 hOffset; /*!< height offset for window */
+	#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
+		HGLRC ctx; /*!< source graphics context */
+	#elif defined(RGFW_OSMESA)
+		OSMesaContext ctx;
+	#elif defined(RGFW_DIRECTX)
+		IDXGISwapChain* swapchain;
+		ID3D11RenderTargetView* renderTargetView;
+		ID3D11DepthStencilView* pDepthStencilView;
+	#elif defined(RGFW_EGL)
+		EGLSurface EGL_surface;
+		EGLDisplay EGL_display;
+		EGLContext EGL_context;
+	#endif
+
+	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
+		HDC hdcMem;
+		HBITMAP bitmap;
+	#endif
+	RGFW_area maxSize, minSize; /*!< for setting max/min resize (RGFW_WINDOWS) */
+#elif defined(RGFW_X11)
+	Display* display; /*!< source display */
+	Window window; /*!< source window */
+	#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
+		GLXContext ctx; /*!< source graphics context */
+	#elif defined(RGFW_OSMESA)
+		OSMesaContext ctx;
+	#elif defined(RGFW_EGL)
+		EGLSurface EGL_surface;
+		EGLDisplay EGL_display;
+		EGLContext EGL_context;
+	#endif
+
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
+		XImage* bitmap;
+		GC gc;
+#endif
+#elif defined(RGFW_WAYLAND)
+	struct wl_display* display;
+	struct wl_surface* surface;
+	struct wl_buffer* wl_buffer;
+	struct wl_keyboard* keyboard;
+
+	struct xdg_surface* xdg_surface;
+	struct xdg_toplevel* xdg_toplevel;
+	struct zxdg_toplevel_decoration_v1* decoration;
+	RGFW_Event events[20];
+		i32 eventLen;
+		size_t eventIndex;
+	#if defined(RGFW_EGL)
+			struct wl_egl_window* window;
+			EGLSurface EGL_surface;
+			EGLDisplay EGL_display;
+			EGLContext EGL_context;
+	#elif defined(RGFW_OSMESA)
+		OSMesaContext ctx;
+	#endif
+#elif defined(RGFW_MACOS)
+	u32 display;
+	void* displayLink;
+	void* window;
+	b8 dndPassed;
+#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
+		void* ctx; /*!< source graphics context */
+#elif defined(RGFW_OSMESA)
+		OSMesaContext ctx;
+#elif defined(RGFW_EGL)
+		EGLSurface EGL_surface;
+		EGLDisplay EGL_display;
+		EGLContext EGL_context;
+#endif
+
+	void* view; /*apple viewpoint thingy*/
+
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
+		void* bitmap; /*!< API's bitmap for storing or managing */
+		void* image;
+#endif
+#elif defined(RGFW_WEBASM)
+	EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
+#endif
+} RGFW_window_src;
+
+
+
+typedef struct RGFW_window {
+	RGFW_window_src src; /*!< src window data */
+
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
+	u8* buffer; /*!< buffer for non-GPU systems (OSMesa, basic software rendering) */
+	/* when rendering using RGFW_BUFFER, the buffer is in the RGBA format */
+#endif
+	void* userPtr; /* ptr for usr data */
+	
+	RGFW_Event event; /*!< current event */
+
+	RGFW_rect r; /*!< the x, y, w and h of the struct */
+	
+	RGFW_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */
+	
+	u32 _winArgs; /*!< windows args (for RGFW to check) */
+} RGFW_window; /*!< Window structure for managing the window */
+
+#if defined(RGFW_X11) || defined(RGFW_MACOS)
+	typedef u64 RGFW_thread; /*!< thread type unix */
+#else
+	typedef void* RGFW_thread; /*!< thread type for window */
+#endif
+
+/** * @defgroup Window_management
+* @{ */ 
+
+
+/*! 
+ * the class name for X11 and WinAPI. apps with the same class will be grouped by the WM
+ * by default the class name will == the root window's name
+*/
+RGFWDEF void RGFW_setClassName(char* name);
+
+/*! this has to be set before createWindow is called, else the fulscreen size is used */
+RGFWDEF void RGFW_setBufferSize(RGFW_area size); /*!< the buffer cannot be resized (by RGFW) */
+
+RGFWDEF RGFW_window* RGFW_createWindow(
+	const char* name, /* name of the window */
+	RGFW_rect rect, /* rect of window */
+	u16 args /* extra arguments (NULL / (u16)0 means no args used)*/
+); /*!< function to create a window struct */
+
+/*! get the size of the screen to an area struct */
+RGFWDEF RGFW_area RGFW_getScreenSize(void);
+
+/*!
+	this function checks an *individual* event (and updates window structure attributes)
+	this means, using this function without a while loop may cause event lag
+
+	ex.
+
+	while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one]
+
+	this function is optional if you choose to use event callbacks, 
+	although you still need some way to tell RGFW to process events eg. `RGFW_window_checkEvents`
+*/
+
+RGFWDEF RGFW_Event* RGFW_window_checkEvent(RGFW_window* win); /*!< check current event (returns a pointer to win->event or NULL if there is no event)*/
+
+/*!
+	for RGFW_window_eventWait and RGFW_window_checkEvents
+	waitMS -> Allows th	e function to keep checking for events even after `RGFW_window_checkEvent == NULL`
+			  if waitMS == 0, the loop will not wait for events
+			  if waitMS == a positive integer, the loop will wait that many miliseconds after there are no more events until it returns
+			  if waitMS == a negative integer, the loop will not return until it gets another event
+*/
+typedef RGFW_ENUM(i32, RGFW_eventWait) {
+	RGFW_NEXT = -1,
+	RGFW_NO_WAIT = 0
+};
+
+/*! sleep until RGFW gets an event or the timer ends (defined by OS) */
+RGFWDEF void RGFW_window_eventWait(RGFW_window* win, i32 waitMS);
+
+/*!
+	check all the events until there are none left, 
+	this should only be used if you're using callbacks only
+*/
+RGFWDEF void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS);
+
+/*! 
+	Tell RGFW_window_eventWait to stop waiting, to be ran from another thread
+*/
+RGFWDEF void RGFW_stopCheckEvents(void);
+
+/*! window managment functions*/
+RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */
+
+/*! moves window to a given point */
+RGFWDEF void RGFW_window_move(RGFW_window* win,
+	RGFW_point v/*!< new pos*/
+);
+
+#ifndef RGFW_NO_MONITOR
+	/*! move to a specific monitor */
+	RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m /* monitor */);
+#endif
+
+/*! resize window to a current size/area */
+RGFWDEF void RGFW_window_resize(RGFW_window* win, /*!< source window */
+	RGFW_area a/*!< new size*/
+);
+
+/*! set the minimum size a user can shrink a window to a given size/area */
+RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a);
+/*! set the minimum size a user can extend a window to a given size/area */
+RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a);
+
+RGFWDEF void RGFW_window_maximize(RGFW_window* win); /*!< maximize the window size */
+RGFWDEF void RGFW_window_minimize(RGFW_window* win); /*!< minimize the window (in taskbar (per OS))*/
+RGFWDEF void RGFW_window_restore(RGFW_window* win); /*!< restore the window from minimized (per OS)*/
+
+/*! if the window should have a border or not (borderless) based on bool value of `border` */
+RGFWDEF void RGFW_window_setBorder(RGFW_window* win, b8 border);
+
+/*! turn on / off dnd (RGFW_ALLOW_DND stil must be passed to the window)*/
+RGFWDEF void RGFW_window_setDND(RGFW_window* win, b8 allow);
+
+#ifndef RGFW_NO_PASSTHROUGH
+	/*!! turn on / off mouse passthrough */
+	RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough);
+#endif 
+
+/*! rename window to a given string */
+RGFWDEF void RGFW_window_setName(RGFW_window* win,
+	char* name
+);
+
+RGFWDEF void RGFW_window_setIcon(RGFW_window* win, /*!< source window */
+	u8* icon /*!< icon bitmap */,
+	RGFW_area a /*!< width and height of the bitmap*/,
+	i32 channels /*!< how many channels the bitmap has (rgb : 3, rgba : 4) */
+); /*!< image resized by default */
+
+/*!< sets mouse to bitmap (very simular to RGFW_window_setIcon), image NOT resized by default*/
+RGFWDEF void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels);
+
+/*!< sets the mouse to a standard API cursor (based on RGFW_MOUSE, as seen at the end of the RGFW_HEADER part of this file) */
+RGFWDEF	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse);
+
+RGFWDEF void RGFW_window_setMouseDefault(RGFW_window* win); /*!< sets the mouse to the default mouse icon */
+/*
+	Locks cursor at the center of the window
+	win->event.point become raw mouse movement data 
+
+	this is useful for a 3D camera
+*/
+RGFWDEF void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area);
+/*! stop holding the mouse and let it move freely */
+RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win);
+
+/*! hide the window */
+RGFWDEF void RGFW_window_hide(RGFW_window* win);
+/*! show the window */
+RGFWDEF void RGFW_window_show(RGFW_window* win);
+
+/*
+	makes it so `RGFW_window_shouldClose` returns true
+	by setting the window event.type to RGFW_quit
+*/
+RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win);
+
+/*! where the mouse is on the screen */
+RGFWDEF RGFW_point RGFW_getGlobalMousePoint(void);
+
+/*! where the mouse is on the window */
+RGFWDEF RGFW_point RGFW_window_getMousePoint(RGFW_window* win);
+
+/*! show the mouse or hide the mouse*/
+RGFWDEF void RGFW_window_showMouse(RGFW_window* win, i8 show);
+/*! move the mouse to a set x, y pos*/
+RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v);
+
+/*! if the window should close (RGFW_close was sent or escape was pressed) */
+RGFWDEF b8 RGFW_window_shouldClose(RGFW_window* win);
+/*! if window is fullscreen'd */
+RGFWDEF b8 RGFW_window_isFullscreen(RGFW_window* win);
+/*! if window is hidden */
+RGFWDEF b8 RGFW_window_isHidden(RGFW_window* win);
+/*! if window is minimized */
+RGFWDEF b8 RGFW_window_isMinimized(RGFW_window* win);
+/*! if window is maximized */
+RGFWDEF b8 RGFW_window_isMaximized(RGFW_window* win);
+
+/** @} */ 
+
+/** * @defgroup Monitor
+* @{ */ 
+
+#ifndef RGFW_NO_MONITOR
+/*
+scale the window to the monitor,
+this is run by default if the user uses the arg `RGFW_SCALE_TO_MONITOR` during window creation
+*/
+RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win);
+/*! get the struct of the window's monitor  */
+RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win);
+#endif
+
+/** @} */ 
+
+/** * @defgroup Input
+* @{ */ 
+
+/*error handling*/
+RGFWDEF b8 RGFW_Error(void); /*!< returns true if an error has occurred (doesn't print errors itself) */
+
+/*! returns true if the key should be shifted */
+RGFWDEF b8 RGFW_shouldShift(u32 keycode, u8 lockState);
+
+/*! get char from RGFW keycode (using a LUT), uses shift'd version if shift = true */
+RGFWDEF char RGFW_keyCodeToChar(u32 keycode, b8 shift);
+/*! get char from RGFW keycode (using a LUT), uses lockState for shouldShift) */
+RGFWDEF char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState);
+
+/*! if window == NULL, it checks if the key is pressed globally. Otherwise, it checks only if the key is pressed while the window in focus.*/
+RGFWDEF b8 RGFW_isPressed(RGFW_window* win, u8 key); /*!< if key is pressed (key code)*/
+
+RGFWDEF b8 RGFW_wasPressed(RGFW_window* win, u8 key); /*!< if key was pressed (checks previous state only) (key code)*/
+
+RGFWDEF b8 RGFW_isHeld(RGFW_window* win, u8 key); /*!< if key is held (key code)*/
+RGFWDEF b8 RGFW_isReleased(RGFW_window* win, u8 key); /*!< if key is released (key code)*/
+
+/* if a key is pressed and then released, pretty much the same as RGFW_isReleased */
+RGFWDEF b8 RGFW_isClicked(RGFW_window* win, u8 key /*!< key code*/);
+
+/*! if a mouse button is pressed */
+RGFWDEF b8 RGFW_isMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ );
+/*! if a mouse button is held */
+RGFWDEF b8 RGFW_isMouseHeld(RGFW_window* win, u8 button /*!< mouse button code */ );
+/*! if a mouse button was released */
+RGFWDEF b8 RGFW_isMouseReleased(RGFW_window* win, u8 button /*!< mouse button code */ );
+/*! if a mouse button was pressed (checks previous state only) */
+RGFWDEF b8 RGFW_wasMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ );
+/** @} */ 
+
+/** * @defgroup Clipboard
+* @{ */ 
+RGFWDEF char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */
+RGFWDEF void RGFW_clipboardFree(char* str); /*!< the string returned from RGFW_readClipboard must be freed */
+
+RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */
+/** @} */ 
+
+/**
+	
+	
+	Event callbacks, 
+	these are completely optional, you can use the normal 
+	RGFW_checkEvent() method if you prefer that
+
+* @defgroup Callbacks
+* @{ 
+*/
+
+/*! RGFW_windowMoved, the window and its new rect value  */
+typedef void (* RGFW_windowmovefunc)(RGFW_window* win, RGFW_rect r);
+/*! RGFW_windowResized, the window and its new rect value  */
+typedef void (* RGFW_windowresizefunc)(RGFW_window* win, RGFW_rect r);
+/*! RGFW_quit, the window that was closed */
+typedef void (* RGFW_windowquitfunc)(RGFW_window* win);
+/*! RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its inFocus */
+typedef void (* RGFW_focusfunc)(RGFW_window* win, b8 inFocus);
+/*! RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */
+typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, RGFW_point point, b8 status);
+/*! RGFW_mousePosChanged, the window that the move happened on and the new point of the mouse  */
+typedef void (* RGFW_mouseposfunc)(RGFW_window* win, RGFW_point point);
+/*! RGFW_dnd_init, the window, the point of the drop on the windows */
+typedef void (* RGFW_dndInitfunc)(RGFW_window* win, RGFW_point point);
+/*! RGFW_windowRefresh, the window that needs to be refreshed */
+typedef void (* RGFW_windowrefreshfunc)(RGFW_window* win);
+/*! RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the keycode, the string version, the state of mod keys, if it was a press (else it's a release) */
+typedef void (* RGFW_keyfunc)(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed);
+/*! RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release)  */
+typedef void (* RGFW_mousebuttonfunc)(RGFW_window* win, u8 button, double scroll, b8 pressed);
+/*! RGFW_jsButtonPressed / RGFW_jsButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */
+typedef void (* RGFW_jsButtonfunc)(RGFW_window* win, u16 joystick, u8 button, b8 pressed);
+/*! RGFW_jsAxisMove, the window that got the event, the joystick in question, the axis values and the amount of axises */
+typedef void (* RGFW_jsAxisfunc)(RGFW_window* win, u16 joystick, RGFW_point axis[2], u8 axisesCount);
+
+
+/*!  RGFW_dnd, the window that had the drop, the drop data and the amount files dropped returns previous callback function (if it was set) */
+#ifdef RGFW_ALLOC_DROPFILES
+	typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount);
+#else
+	typedef void (* RGFW_dndfunc)(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount);
+#endif
+/*! set callback for a window move event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func);
+/*! set callback for a window resize event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc func);
+/*! set callback for a window quit event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_windowquitfunc RGFW_setWindowQuitCallback(RGFW_windowquitfunc func);
+/*! set callback for a mouse move event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_mouseposfunc RGFW_setMousePosCallback(RGFW_mouseposfunc func);
+/*! set callback for a window refresh event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_windowrefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func);
+/*! set callback for a window focus change event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func);
+/*! set callback for a mouse notify event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func);
+/*! set callback for a drop event event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func);
+/*! set callback for a start of a drop event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func);
+/*! set callback for a key (press / release ) event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func);
+/*! set callback for a mouse button (press / release ) event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_mousebuttonfunc RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func);
+/*! set callback for a controller button (press / release ) event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func);
+/*! set callback for a joystick axis mov event returns previous callback function (if it was set)  */
+RGFWDEF RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc func);
+
+/** @} */ 
+
+/** * @defgroup Threads
+* @{ */ 
+
+#ifndef RGFW_NO_THREADS
+	/*! threading functions*/
+
+	/*! NOTE! (for X11/linux) : if you define a window in a thread, it must be run after the original thread's window is created or else there will be a memory error */
+	/*
+		I'd suggest you use sili's threading functions instead
+		if you're going to use sili
+		which is a good idea generally
+	*/
+
+	#if defined(__unix__) || defined(__APPLE__) || defined(RGFW_WEBASM) 
+		typedef void* (* RGFW_threadFunc_ptr)(void*);
+	#else
+		typedef DWORD (__stdcall *RGFW_threadFunc_ptr) (LPVOID lpThreadParameter);  
+	#endif
+
+	RGFWDEF RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args); /*!< create a thread*/
+	RGFWDEF void RGFW_cancelThread(RGFW_thread thread); /*!< cancels a thread*/
+	RGFWDEF void RGFW_joinThread(RGFW_thread thread); /*!< join thread to current thread */
+	RGFWDEF void RGFW_setThreadPriority(RGFW_thread thread, u8 priority); /*!< sets the priority priority  */
+#endif
+
+/** @} */ 
+
+/** * @defgroup joystick
+* @{ */ 
+
+/*! joystick count starts at 0*/
+/*!< register joystick to window based on a number (the number is based on when it was connected eg. /dev/js0)*/
+RGFWDEF u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber);
+RGFWDEF u16 RGFW_registerJoystickF(RGFW_window* win, char* file);
+
+RGFWDEF u32 RGFW_isPressedJS(RGFW_window* win, u16 controller, u8 button);
+
+/** @} */ 
+
+/** * @defgroup graphics_API
+* @{ */ 
+
+/*!< make the window the current opengl drawing context
+
+	NOTE:
+ 	if you want to switch the graphics context's thread, 
+	you have to run RGFW_window_makeCurrent(NULL); on the old thread
+	then RGFW_window_makeCurrent(valid_window) on the new thread
+*/
+RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win);
+
+/*< updates fps / sets fps to cap (must by ran manually by the user at the end of a frame), returns current fps */
+RGFWDEF u32 RGFW_window_checkFPS(RGFW_window* win, u32 fpsCap);
+
+/* supports openGL, directX, OSMesa, EGL and software rendering */
+RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /*!< swap the rendering buffer */
+RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval);
+
+RGFWDEF void RGFW_window_setGPURender(RGFW_window* win, i8 set);
+RGFWDEF void RGFW_window_setCPURender(RGFW_window* win, i8 set);
+
+/*! native API functions */
+#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
+	/*! OpenGL init hints */
+	RGFWDEF void RGFW_setGLStencil(i32 stencil); /*!< set stencil buffer bit size (8 by default) */
+	RGFWDEF void RGFW_setGLSamples(i32 samples); /*!< set number of sampiling buffers (4 by default) */
+	RGFWDEF void RGFW_setGLStereo(i32 stereo); /*!< use GL_STEREO (GL_FALSE by default) */
+	RGFWDEF void RGFW_setGLAuxBuffers(i32 auxBuffers); /*!< number of aux buffers (0 by default) */
+
+	/*! which profile to use for the opengl verion */
+	typedef RGFW_ENUM(u8, RGFW_GL_profile)  { RGFW_GL_CORE = 0,  RGFW_GL_COMPATIBILITY  };
+	/*! Set OpenGL version hint (core or compatibility profile)*/
+	RGFWDEF void RGFW_setGLVersion(RGFW_GL_profile profile, i32 major, i32 minor);
+	RGFWDEF void RGFW_setDoubleBuffer(b8 useDoubleBuffer); 
+    RGFWDEF void* RGFW_getProcAddress(const char* procname); /*!< get native opengl proc address */
+    RGFWDEF void RGFW_window_makeCurrent_OpenGL(RGFW_window* win); /*!< to be called by RGFW_window_makeCurrent */
+#elif defined(RGFW_DIRECTX)
+	typedef struct {
+		IDXGIFactory* pFactory;
+		IDXGIAdapter* pAdapter;
+		ID3D11Device* pDevice;
+		ID3D11DeviceContext* pDeviceContext;
+	} RGFW_directXinfo;
+
+	/*
+		RGFW stores a global instance of RGFW_directXinfo,
+		you can use this function to get a pointer the instance
+	*/
+	RGFWDEF RGFW_directXinfo* RGFW_getDirectXInfo(void);
+#endif
+
+/** @} */ 
+
+/** * @defgroup Supporting
+* @{ */ 
+RGFWDEF u64 RGFW_getTime(void); /*!< get time in seconds */
+RGFWDEF u64 RGFW_getTimeNS(void); /*!< get time in nanoseconds */
+RGFWDEF void RGFW_sleep(u64 milisecond); /*!< sleep for a set time */
+
+/*!
+	key codes and mouse icon enums
+*/
+
+typedef RGFW_ENUM(u8, RGFW_Key) {
+	RGFW_KEY_NULL = 0,
+	RGFW_Escape,
+	RGFW_F1,
+	RGFW_F2,
+	RGFW_F3,
+	RGFW_F4,
+	RGFW_F5,
+	RGFW_F6,
+	RGFW_F7,
+	RGFW_F8,
+	RGFW_F9,
+	RGFW_F10,
+	RGFW_F11,
+	RGFW_F12,
+
+	RGFW_Backtick,
+
+	RGFW_0,
+	RGFW_1,
+	RGFW_2,
+	RGFW_3,
+	RGFW_4,
+	RGFW_5,
+	RGFW_6,
+	RGFW_7,
+	RGFW_8,
+	RGFW_9,
+
+	RGFW_Minus,
+	RGFW_Equals,
+	RGFW_BackSpace,
+	RGFW_Tab,
+	RGFW_CapsLock,
+	RGFW_ShiftL,
+	RGFW_ControlL,
+	RGFW_AltL,
+	RGFW_SuperL,
+	RGFW_ShiftR,
+	RGFW_ControlR,
+	RGFW_AltR,
+	RGFW_SuperR,
+	RGFW_Space,
+
+	RGFW_a,
+	RGFW_b,
+	RGFW_c,
+	RGFW_d,
+	RGFW_e,
+	RGFW_f,
+	RGFW_g,
+	RGFW_h,
+	RGFW_i,
+	RGFW_j,
+	RGFW_k,
+	RGFW_l,
+	RGFW_m,
+	RGFW_n,
+	RGFW_o,
+	RGFW_p,
+	RGFW_q,
+	RGFW_r,
+	RGFW_s,
+	RGFW_t,
+	RGFW_u,
+	RGFW_v,
+	RGFW_w,
+	RGFW_x,
+	RGFW_y,
+	RGFW_z,
+
+	RGFW_Period,
+	RGFW_Comma,
+	RGFW_Slash,
+	RGFW_Bracket,
+	RGFW_CloseBracket,
+	RGFW_Semicolon,
+	RGFW_Return,
+	RGFW_Quote,
+	RGFW_BackSlash,
+
+	RGFW_Up,
+	RGFW_Down,
+	RGFW_Left,
+	RGFW_Right,
+
+	RGFW_Delete,
+	RGFW_Insert,
+	RGFW_End,
+	RGFW_Home,
+	RGFW_PageUp,
+	RGFW_PageDown,
+
+	RGFW_Numlock,
+	RGFW_KP_Slash,
+	RGFW_Multiply,
+	RGFW_KP_Minus,
+	RGFW_KP_1,
+	RGFW_KP_2,
+	RGFW_KP_3,
+	RGFW_KP_4,
+	RGFW_KP_5,
+	RGFW_KP_6,
+	RGFW_KP_7,
+	RGFW_KP_8,
+	RGFW_KP_9,
+	RGFW_KP_0,
+	RGFW_KP_Period,
+	RGFW_KP_Return,
+
+	final_key,
+};
+
+
+typedef RGFW_ENUM(u8, RGFW_mouseIcons) {
+	RGFW_MOUSE_NORMAL = 0,
+	RGFW_MOUSE_ARROW,
+	RGFW_MOUSE_IBEAM,
+	RGFW_MOUSE_CROSSHAIR,
+	RGFW_MOUSE_POINTING_HAND,
+	RGFW_MOUSE_RESIZE_EW,
+	RGFW_MOUSE_RESIZE_NS,
+	RGFW_MOUSE_RESIZE_NWSE,
+	RGFW_MOUSE_RESIZE_NESW,
+	RGFW_MOUSE_RESIZE_ALL,
+	RGFW_MOUSE_NOT_ALLOWED,
+};
+
+/** @} */ 
+
+#endif /* RGFW_HEADER */
+
+/*
+Example to get you started :
+
+linux : gcc main.c -lX11 -lXcursor -lGL
+windows : gcc main.c -lopengl32 -lshell32 -lgdi32
+macos : gcc main.c -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo
+
+#define RGFW_IMPLEMENTATION
+#include "RGFW.h"
+
+u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF,    0xFF, 0x00, 0x00, 0xFF,     0xFF, 0x00, 0x00, 0xFF,   0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,     0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF};
+
+int main() {
+	RGFW_window* win = RGFW_createWindow("name", RGFW_RECT(500, 500, 500, 500), (u64)0);
+
+	RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4);
+
+	for (;;) {
+		RGFW_window_checkEvent(win); // NOTE: checking events outside of a while loop may cause input lag
+		if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape))
+			break;
+
+		RGFW_window_swapBuffers(win);
+
+		glClearColor(0xFF, 0XFF, 0xFF, 0xFF);
+		glClear(GL_COLOR_BUFFER_BIT);
+	}
+
+	RGFW_window_close(win);
+}
+
+	compiling :
+
+	if you wish to compile the library all you have to do is create a new file with this in it
+
+	rgfw.c
+	#define RGFW_IMPLEMENTATION
+	#include "RGFW.h"
+
+	then you can use gcc (or whatever compile you wish to use) to compile the library into object file
+
+	ex. gcc -c RGFW.c -fPIC
+
+	after you compile the library into an object file, you can also turn the object file into an static or shared library
+
+	(commands ar and gcc can be replaced with whatever equivalent your system uses)
+	static : ar rcs RGFW.a RGFW.o
+	shared :
+		windows:
+			gcc -shared RGFW.o -lopengl32 -lshell32 -lgdi32 -o RGFW.dll
+		linux:
+			gcc -shared RGFW.o -lX11 -lXcursor -lGL -o RGFW.so
+		macos:
+			gcc -shared RGFW.o -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo
+*/
+
+#ifdef RGFW_X11
+	#define RGFW_OS_BASED_VALUE(l, w, m, h, ww) l
+#elif defined(RGFW_WINDOWS)
+	#define RGFW_OS_BASED_VALUE(l, w, m, h, ww) w
+#elif defined(RGFW_MACOS)
+	#define RGFW_OS_BASED_VALUE(l, w, m, h, ww) m
+#elif defined(RGFW_WEBASM)
+	#define RGFW_OS_BASED_VALUE(l, w, m, h, ww) h
+#elif defined(RGFW_WAYLAND)
+    #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) ww  
+#endif
+
+
+#ifdef RGFW_IMPLEMENTATION
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+/*
+RGFW_IMPLEMENTATION starts with generic RGFW defines
+
+This is the start of keycode data
+
+	Why not use macros instead of the numbers itself?
+	Windows -> Not all virtual keys are macros (VK_0 - VK_1, VK_a - VK_z)
+	Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size
+	MacOS -> windows and linux already don't have keycodes as macros, so there's no point
+*/
+
+
+
+/* 
+	the c++ compiler doesn't support setting up an array like, 
+	we'll have to do it during runtime using a function & this messy setup
+*/
+#ifndef __cplusplus
+#define RGFW_NEXT ,
+#define RGFW_MAP
+#else 
+#define RGFW_NEXT ;
+#define RGFW_MAP RGFW_keycodes
+#endif
+
+#ifdef RGFW_WAYLAND
+#include <linux/input-event-codes.h>
+#endif
+
+u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(136, 337, 128, DOM_VK_WIN_OEM_CLEAR + 1, 130)] = {
+#ifdef __cplusplus
+	0
+};
+void RGFW_init_keys(void) {
+#endif
+	RGFW_MAP [RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE)] = RGFW_Backtick 		RGFW_NEXT
+
+	RGFW_MAP [RGFW_OS_BASED_VALUE(19, 0x30, 29, DOM_VK_0, KEY_0)] = RGFW_0 					RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(10, 0x31, 18, DOM_VK_1, KEY_1)] = RGFW_1						RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(11, 0x32, 19, DOM_VK_2, KEY_2)] = RGFW_2						RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(12, 0x33, 20, DOM_VK_3, KEY_3)] = RGFW_3						RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(13, 0x34, 21, DOM_VK_4, KEY_4)] = RGFW_4						RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(14, 0x35, 23, DOM_VK_5, KEY_5)] = RGFW_5                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(15, 0x36, 22, DOM_VK_6, KEY_6)] = RGFW_6                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(16, 0x37, 26, DOM_VK_7, KEY_7)] = RGFW_7                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(17, 0x38, 28, DOM_VK_8, KEY_8)] = RGFW_8                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(18, 0x39, 25, DOM_VK_9, KEY_9)] = RGFW_9,
+
+	RGFW_MAP [RGFW_OS_BASED_VALUE(65, 0x20, 49, DOM_VK_SPACE, KEY_SPACE)] = RGFW_Space,
+
+	RGFW_MAP [RGFW_OS_BASED_VALUE(38, 0x41, 0, DOM_VK_A, KEY_A)] = RGFW_a                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(56, 0x42, 11, DOM_VK_B, KEY_B)] = RGFW_b                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(54, 0x43, 8, DOM_VK_C, KEY_C)] = RGFW_c                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(40, 0x44, 2, DOM_VK_D, KEY_D)] = RGFW_d                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(26, 0x45, 14, DOM_VK_E, KEY_E)] = RGFW_e                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(41, 0x46, 3, DOM_VK_F, KEY_F)] = RGFW_f                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(42, 0x47, 5, DOM_VK_G, KEY_G)] = RGFW_g                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(43, 0x48, 4, DOM_VK_H, KEY_H)] = RGFW_h                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(31, 0x49, 34, DOM_VK_I, KEY_I)] = RGFW_i                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(44, 0x4A, 38, DOM_VK_J, KEY_J)] = RGFW_j                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(45, 0x4B, 40, DOM_VK_K, KEY_K)] = RGFW_k                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(46, 0x4C, 37, DOM_VK_L, KEY_L)] = RGFW_l                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(58, 0x4D, 46, DOM_VK_M, KEY_M)] = RGFW_m                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(57, 0x4E, 45, DOM_VK_N, KEY_N)] = RGFW_n                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(32, 0x4F, 31, DOM_VK_O, KEY_O)] = RGFW_o                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(33, 0x50, 35, DOM_VK_P, KEY_P)] = RGFW_p                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(24, 0x51, 12, DOM_VK_Q, KEY_Q)] = RGFW_q                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(27, 0x52, 15, DOM_VK_R, KEY_R)] = RGFW_r                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(39, 0x53, 1, DOM_VK_S, KEY_S)] = RGFW_s                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(28, 0x54, 17, DOM_VK_T, KEY_T)] = RGFW_t                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(30, 0x55, 32, DOM_VK_U, KEY_U)] = RGFW_u                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(55, 0x56, 9, DOM_VK_V, KEY_V)] = RGFW_v                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(25, 0x57, 13, DOM_VK_W, KEY_W)] = RGFW_w                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(53, 0x58, 7, DOM_VK_X, KEY_X)] = RGFW_x                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(29, 0x59, 16, DOM_VK_Y, KEY_Y)] = RGFW_y                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(52, 0x5A, 6, DOM_VK_Z, KEY_Z)] = RGFW_z,
+
+	RGFW_MAP [RGFW_OS_BASED_VALUE(60, 190, 47, DOM_VK_PERIOD, KEY_DOT)] = RGFW_Period             			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(59, 188, 43, DOM_VK_COMMA, KEY_COMMA)] = RGFW_Comma               			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(61, 191, 44, DOM_VK_SLASH, KEY_SLASH)] = RGFW_Slash               			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(34, 219, 33, DOM_VK_OPEN_BRACKET, KEY_LEFTBRACE)] = RGFW_Bracket      			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(35, 221, 30, DOM_VK_CLOSE_BRACKET, KEY_RIGHTBRACE)] = RGFW_CloseBracket             RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(47, 186, 41, DOM_VK_SEMICOLON, KEY_SEMICOLON)] = RGFW_Semicolon                 RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(48, 222, 39, DOM_VK_QUOTE, KEY_APOSTROPHE)] = RGFW_Quote                 			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(51, 322, 42, DOM_VK_BACK_SLASH, KEY_BACKSLASH)] = RGFW_BackSlash,
+	
+	RGFW_MAP [RGFW_OS_BASED_VALUE(36, 0x0D, 36, DOM_VK_RETURN, KEY_ENTER)] = RGFW_Return              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(119, 0x2E, 118, DOM_VK_DELETE, KEY_DELETE)] = RGFW_Delete                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(77, 0x90, 72, DOM_VK_NUM_LOCK, KEY_NUMLOCK)] = RGFW_Numlock               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(106, 0x6F, 82, DOM_VK_DIVIDE, KEY_KPSLASH)] = RGFW_KP_Slash               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(63, 0x6A, 76, DOM_VK_MULTIPLY, KEY_KPASTERISK)] = RGFW_Multiply              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(82, 0x6D, 67, DOM_VK_SUBTRACT, KEY_KPMINUS)] = RGFW_KP_Minus              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(87, 0x61, 84, DOM_VK_NUMPAD1, KEY_KP1)] = RGFW_KP_1               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(88, 0x62, 85, DOM_VK_NUMPAD2, KEY_KP2)] = RGFW_KP_2               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(89, 0x63, 86, DOM_VK_NUMPAD3, KEY_KP3)] = RGFW_KP_3               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(83, 0x64, 87, DOM_VK_NUMPAD4, KEY_KP4)] = RGFW_KP_4               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(84, 0x65, 88, DOM_VK_NUMPAD5, KEY_KP5)] = RGFW_KP_5               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(85, 0x66, 89, DOM_VK_NUMPAD6, KEY_KP6)] = RGFW_KP_6               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(79, 0x67, 90, DOM_VK_NUMPAD7, KEY_KP7)] = RGFW_KP_7               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(80, 0x68, 92, DOM_VK_NUMPAD8, KEY_KP8)] = RGFW_KP_8               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(81, 0x69, 93, DOM_VK_NUMPAD9, KEY_KP9)] = RGFW_KP_9               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(90, 0x60, 83, DOM_VK_NUMPAD0, KEY_KP0)] = RGFW_KP_0               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(91, 0x6E, 65, DOM_VK_DECIMAL, KEY_KPDOT)] = RGFW_KP_Period              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(104, 0x92, 77, 0, KEY_KPENTER)] = RGFW_KP_Return,
+	
+	RGFW_MAP [RGFW_OS_BASED_VALUE(20, 189, 27, DOM_VK_HYPHEN_MINUS, KEY_MINUS)] = RGFW_Minus              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(21, 187, 24, DOM_VK_EQUALS, KEY_EQUAL)] = RGFW_Equals               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(22, 8, 51, DOM_VK_BACK_SPACE, KEY_BACKSPACE)] = RGFW_BackSpace              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(23, 0x09, 48, DOM_VK_TAB, KEY_TAB)] = RGFW_Tab                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(66, 20, 57, DOM_VK_CAPS_LOCK, KEY_CAPSLOCK)] = RGFW_CapsLock               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(50, 0x10, 56, DOM_VK_SHIFT, KEY_LEFTSHIFT)] = RGFW_ShiftL               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(37, 0x11, 59, DOM_VK_CONTROL, KEY_LEFTCTRL)] = RGFW_ControlL               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(64,0x12, 58, DOM_VK_ALT, KEY_LEFTALT)] = RGFW_AltL                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x5B, 55, DOM_VK_WIN, KEY_LEFTMETA)] = RGFW_SuperL,
+	
+	#if !defined(RGFW_WINDOWS) && !defined(RGFW_MACOS) && !defined(RGFW_WEBASM)
+	RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11, 59, 0, KEY_RIGHTCTRL)] = RGFW_ControlR               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(135, 0xA4, 55, 0, KEY_RIGHTMETA)] = RGFW_SuperR,
+	RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x5C, 56, 0, KEY_RIGHTSHIFT)] = RGFW_ShiftR              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(108, 165, 58, 0, KEY_RIGHTALT)] = RGFW_AltR,
+	#endif
+
+	RGFW_MAP [RGFW_OS_BASED_VALUE(67, 0x70, 127, DOM_VK_F1, KEY_F1)] = RGFW_F1                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(68, 0x71, 121, DOM_VK_F2, KEY_F2)] = RGFW_F2                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(69, 0x72, 100, DOM_VK_F3, KEY_F3)] = RGFW_F3                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(70, 0x73, 119, DOM_VK_F4, KEY_F4)] = RGFW_F4                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(71, 0x74, 97, DOM_VK_F5, KEY_F5)] = RGFW_F5              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(72, 0x75, 98, DOM_VK_F6, KEY_F6)] = RGFW_F6              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(73, 0x76, 99, DOM_VK_F7, KEY_F7)] = RGFW_F7              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(74, 0x77, 101, DOM_VK_F8, KEY_F8)] = RGFW_F8                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x78, 102, DOM_VK_F9, KEY_F9)] = RGFW_F9                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x79, 110, DOM_VK_F10, KEY_F10)] = RGFW_F10               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x7A, 104, DOM_VK_F11, KEY_F11)] = RGFW_F11               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x7B, 112, DOM_VK_F12, KEY_F12)] = RGFW_F12               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x26, 126, DOM_VK_UP, KEY_UP)] = RGFW_Up                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x28, 125, DOM_VK_DOWN, KEY_DOWN)] = RGFW_Down                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x25, 123, DOM_VK_LEFT, KEY_LEFT)] = RGFW_Left                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(114, 0x27, 124, DOM_VK_RIGHT, KEY_RIGHT)] = RGFW_Right              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(118, 0x2D, 115, DOM_VK_INSERT, KEY_INSERT)] = RGFW_Insert                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(115, 0x23, 120, DOM_VK_END, KEY_END)] = RGFW_End                  		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(112, 336, 117, DOM_VK_PAGE_UP, KEY_PAGEUP)] = RGFW_PageUp                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(117, 325, 122, DOM_VK_PAGE_DOWN, KEY_PAGEDOWN)] = RGFW_PageDown            RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x1B, 53, DOM_VK_ESCAPE, KEY_ESC)] = RGFW_Escape                   		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x24, 116, DOM_VK_HOME, KEY_HOME)] = RGFW_Home                    		RGFW_NEXT
+#ifndef __cplusplus
+};
+#else 
+}
+#endif
+
+#undef RGFW_NEXT
+#undef RGFW_MAP
+
+typedef struct {
+	b8 current  : 1;
+	b8 prev  : 1;
+} RGFW_keyState;
+
+RGFW_keyState RGFW_keyboard[final_key] = { {0, 0} };
+
+RGFWDEF u32 RGFW_apiKeyCodeToRGFW(u32 keycode);
+
+u32 RGFW_apiKeyCodeToRGFW(u32 keycode) {
+	#ifdef __cplusplus
+	if (RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE) != RGFW_Backtick) {
+		RGFW_init_keys();
+	}
+	#endif
+
+	/* make sure the key isn't out of bounds */
+	if (keycode > sizeof(RGFW_keycodes) / sizeof(u8))
+		return 0;
+	
+	return RGFW_keycodes[keycode];
+}
+
+RGFWDEF void RGFW_resetKey(void);
+void RGFW_resetKey(void) {
+	size_t len = final_key; /*!< last_key == length */
+	
+	size_t i; /*!< reset each previous state  */
+	for (i = 0; i < len; i++)
+		RGFW_keyboard[i].prev = 0;
+}
+
+b8 RGFW_shouldShift(u32 keycode, u8 lockState) {
+    #define RGFW_xor(x, y) (( (x) && (!(y)) ) ||  ((y) && (!(x)) ))
+    b8 caps4caps = (lockState & RGFW_CAPSLOCK) && ((keycode >= RGFW_a) && (keycode <= RGFW_z));
+    b8 shouldShift = RGFW_xor((RGFW_isPressed(NULL, RGFW_ShiftL) || RGFW_isPressed(NULL, RGFW_ShiftR)), caps4caps);
+    #undef RGFW_xor
+
+	return shouldShift;
+}	
+
+char RGFW_keyCodeToChar(u32 keycode, b8 shift) {
+    static const char map[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '`', '0', '1', '2', '3', '4', '5', '6', '7', '8', 
+        '9', '-', '=', 0, '\t',  0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+        'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '.', ',', '/', '[', ']',  ';', '\n', '\'', '\\', 
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  '/', '*', '-', '1', '2', '3',  '3', '5', '6', '7', '8',  '9', '0', '\n'
+    };
+
+    static const char mapCaps[] = {
+        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, ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+        'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+        'X', 'Y', 'Z', '>', '<', '?', '{', '}',  ':', '\n', '"', '|', 
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '?', '*', '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+
+    if (shift == RGFW_FALSE)
+        return map[keycode]; 
+    return mapCaps[keycode];
+}
+
+char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState) { return RGFW_keyCodeToChar(keycode, RGFW_shouldShift(keycode, lockState)); }
+
+/*
+	this is the end of keycode data
+*/
+
+/* joystick data */
+u8 RGFW_jsPressed[4][16]; /*!< if a key is currently pressed or not (per joystick) */
+
+i32 RGFW_joysticks[4]; /*!< limit of 4 joysticks at a time */
+u16 RGFW_joystickCount; /*!< the actual amount of joysticks */
+
+/* 
+	event callback defines start here
+*/
+
+
+/*
+	These exist to avoid the 
+	if (func == NULL) check 
+	for (allegedly) better performance
+*/
+void RGFW_windowmovefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
+void RGFW_windowresizefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
+void RGFW_windowquitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_focusfuncEMPTY(RGFW_window* win, b8 inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);}
+void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_point point, b8 status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);}
+void RGFW_mouseposfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);}
+void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);}
+void RGFW_windowrefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); }
+void RGFW_keyfuncEMPTY(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(keycode); RGFW_UNUSED(keyName); RGFW_UNUSED(lockState); RGFW_UNUSED(pressed);}
+void RGFW_mousebuttonfuncEMPTY(RGFW_window* win, u8 button, double scroll, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);}
+void RGFW_jsButtonfuncEMPTY(RGFW_window* win, u16 joystick, u8 button, b8 pressed){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(button); RGFW_UNUSED(pressed); }
+void RGFW_jsAxisfuncEMPTY(RGFW_window* win, u16 joystick, RGFW_point axis[2], u8 axisesCount){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); }
+
+#ifdef RGFW_ALLOC_DROPFILES
+void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);}
+#else
+void RGFW_dndfuncEMPTY(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);}
+#endif
+
+RGFW_windowmovefunc RGFW_windowMoveCallback = RGFW_windowmovefuncEMPTY;
+RGFW_windowresizefunc RGFW_windowResizeCallback = RGFW_windowresizefuncEMPTY;
+RGFW_windowquitfunc RGFW_windowQuitCallback = RGFW_windowquitfuncEMPTY;
+RGFW_mouseposfunc RGFW_mousePosCallback = RGFW_mouseposfuncEMPTY;
+RGFW_windowrefreshfunc RGFW_windowRefreshCallback = RGFW_windowrefreshfuncEMPTY;
+RGFW_focusfunc RGFW_focusCallback = RGFW_focusfuncEMPTY;
+RGFW_mouseNotifyfunc RGFW_mouseNotifyCallBack = RGFW_mouseNotifyfuncEMPTY;
+RGFW_dndfunc RGFW_dndCallback = RGFW_dndfuncEMPTY;
+RGFW_dndInitfunc RGFW_dndInitCallback = RGFW_dndInitfuncEMPTY;
+RGFW_keyfunc RGFW_keyCallback = RGFW_keyfuncEMPTY;
+RGFW_mousebuttonfunc RGFW_mouseButtonCallback = RGFW_mousebuttonfuncEMPTY;
+RGFW_jsButtonfunc RGFW_jsButtonCallback = RGFW_jsButtonfuncEMPTY;
+RGFW_jsAxisfunc RGFW_jsAxisCallback = RGFW_jsAxisfuncEMPTY;
+
+void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS) { 
+	RGFW_window_eventWait(win, waitMS);
+
+	while (RGFW_window_checkEvent(win) != NULL && RGFW_window_shouldClose(win) == 0) { 
+		if (win->event.type == RGFW_quit) return; 
+	}
+	
+	#ifdef RGFW_WEBASM /* webasm needs to run the sleep function for asyncify */
+		RGFW_sleep(0);
+	#endif
+}
+
+RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func) { 
+	RGFW_windowmovefunc	prev =  (RGFW_windowMoveCallback == RGFW_windowmovefuncEMPTY) ? NULL : RGFW_windowMoveCallback;
+	RGFW_windowMoveCallback = func;
+	return prev;
+}
+RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc func) {
+    RGFW_windowresizefunc prev = (RGFW_windowResizeCallback == RGFW_windowresizefuncEMPTY) ? NULL : RGFW_windowResizeCallback;
+    RGFW_windowResizeCallback = func;
+    return prev;
+}
+RGFW_windowquitfunc RGFW_setWindowQuitCallback(RGFW_windowquitfunc func) {
+    RGFW_windowquitfunc prev = (RGFW_windowQuitCallback == RGFW_windowquitfuncEMPTY) ? NULL : RGFW_windowQuitCallback;
+    RGFW_windowQuitCallback = func;
+    return prev;
+}
+
+RGFW_mouseposfunc RGFW_setMousePosCallback(RGFW_mouseposfunc func) {
+    RGFW_mouseposfunc prev = (RGFW_mousePosCallback == RGFW_mouseposfuncEMPTY) ? NULL : RGFW_mousePosCallback;
+    RGFW_mousePosCallback = func;
+    return prev;
+}
+RGFW_windowrefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func) {
+    RGFW_windowrefreshfunc prev = (RGFW_windowRefreshCallback == RGFW_windowrefreshfuncEMPTY) ? NULL : RGFW_windowRefreshCallback;
+    RGFW_windowRefreshCallback = func;
+    return prev;
+}
+RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func) {
+    RGFW_focusfunc prev = (RGFW_focusCallback == RGFW_focusfuncEMPTY) ? NULL : RGFW_focusCallback;
+    RGFW_focusCallback = func;
+    return prev;
+}
+
+RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func) {
+    RGFW_mouseNotifyfunc prev = (RGFW_mouseNotifyCallBack == RGFW_mouseNotifyfuncEMPTY) ? NULL : RGFW_mouseNotifyCallBack;
+    RGFW_mouseNotifyCallBack = func;
+    return prev;
+}
+RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func) {
+    RGFW_dndfunc prev = (RGFW_dndCallback == RGFW_dndfuncEMPTY) ? NULL : RGFW_dndCallback;
+    RGFW_dndCallback = func;
+    return prev;
+}
+RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func) {
+    RGFW_dndInitfunc prev = (RGFW_dndInitCallback == RGFW_dndInitfuncEMPTY) ? NULL : RGFW_dndInitCallback;
+    RGFW_dndInitCallback = func;
+    return prev;
+}
+RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func) {
+    RGFW_keyfunc prev = (RGFW_keyCallback == RGFW_keyfuncEMPTY) ? NULL : RGFW_keyCallback;
+    RGFW_keyCallback = func;
+    return prev;
+}
+RGFW_mousebuttonfunc RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func) {
+    RGFW_mousebuttonfunc prev = (RGFW_mouseButtonCallback == RGFW_mousebuttonfuncEMPTY) ? NULL : RGFW_mouseButtonCallback;
+    RGFW_mouseButtonCallback = func;
+    return prev;
+}
+RGFW_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func) {
+    RGFW_jsButtonfunc prev = (RGFW_jsButtonCallback == RGFW_jsButtonfuncEMPTY) ? NULL : RGFW_jsButtonCallback;
+    RGFW_jsButtonCallback = func;
+    return prev;
+}
+RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc func) {
+    RGFW_jsAxisfunc prev = (RGFW_jsAxisCallback == RGFW_jsAxisfuncEMPTY) ? NULL : RGFW_jsAxisCallback;
+    RGFW_jsAxisCallback = func;
+    return prev;
+}
+/* 
+no more event call back defines
+*/
+
+#define RGFW_ASSERT(check, str) {\
+	if (!(check)) { \
+		printf(str); \
+		assert(check); \
+	} \
+}
+
+b8 RGFW_error = 0;
+b8 RGFW_Error(void) { return RGFW_error; }
+
+#define SET_ATTRIB(a, v) { \
+    assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
+    attribs[index++] = a; \
+    attribs[index++] = v; \
+}
+	
+RGFW_area RGFW_bufferSize = {0, 0};
+void RGFW_setBufferSize(RGFW_area size) {
+	RGFW_bufferSize = size;
+}
+
+
+RGFWDEF RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args);
+
+/* do a basic initialization for RGFW_window, this is to standard it for each OS */
+RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) {
+	RGFW_window* win = (RGFW_window*) RGFW_MALLOC(sizeof(RGFW_window)); /*!< make a new RGFW struct */
+
+	/* clear out dnd info */
+#ifdef RGFW_ALLOC_DROPFILES
+	win->event.droppedFiles = (char**) RGFW_MALLOC(sizeof(char*) * RGFW_MAX_DROPS);
+	u32 i;
+	for (i = 0; i < RGFW_MAX_DROPS; i++)
+		win->event.droppedFiles[i] = (char*) RGFW_CALLOC(RGFW_MAX_PATH, sizeof(char));
+#endif
+
+	/* X11 requires us to have a display to get the screen size */
+	#ifndef RGFW_X11 
+	RGFW_area screenR = RGFW_getScreenSize();
+	#else
+	win->src.display = XOpenDisplay(NULL);
+	assert(win->src.display != NULL);
+
+	Screen* scrn = DefaultScreenOfDisplay((Display*)win->src.display);
+	RGFW_area screenR = RGFW_AREA((u32)scrn->width, (u32)scrn->height);
+	#endif
+	
+	/* rect based the requested args */
+	if (args & RGFW_FULLSCREEN)
+		rect = RGFW_RECT(0, 0, screenR.w, screenR.h);
+
+	/* set and init the new window's data */
+	win->r = rect;
+	win->event.inFocus = 1;
+	win->event.droppedFilesCount = 0;
+	RGFW_joystickCount = 0;
+	win->_winArgs = 0;
+	win->event.lockState = 0;
+
+	return win;
+}
+
+#ifndef RGFW_NO_MONITOR
+void RGFW_window_scaleToMonitor(RGFW_window* win) {
+	RGFW_monitor monitor = RGFW_window_getMonitor(win);
+	
+	RGFW_window_resize(win, RGFW_AREA((u32)(monitor.scaleX * (float)win->r.w), (u32)(monitor.scaleX * (float)win->r.h)));
+}
+#endif
+
+RGFW_window* RGFW_root = NULL;
+
+
+#define RGFW_HOLD_MOUSE			(1L<<2) /*!< hold the moues still */
+#define RGFW_MOUSE_LEFT 		(1L<<3) /* if mouse left the window */
+
+#ifdef RGFW_MACOS
+RGFWDEF void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer);
+RGFWDEF void* RGFW_cocoaGetLayer(void);
+#endif
+
+char* RGFW_className = NULL;
+void RGFW_setClassName(char* name) {
+	RGFW_className = name;
+}
+
+void RGFW_clipboardFree(char* str) { RGFW_FREE(str); }
+
+RGFW_keyState RGFW_mouseButtons[5] = { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+
+b8 RGFW_isMousePressed(RGFW_window* win, u8 button) {
+	assert(win != NULL);
+	return RGFW_mouseButtons[button].current && (win != NULL) && win->event.inFocus; 
+}
+b8 RGFW_wasMousePressed(RGFW_window* win, u8 button) {
+	assert(win != NULL); 
+	return RGFW_mouseButtons[button].prev && (win != NULL) && win->event.inFocus; 
+}
+b8 RGFW_isMouseHeld(RGFW_window* win, u8 button) {
+	return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));
+}
+b8 RGFW_isMouseReleased(RGFW_window* win, u8 button) {
+	return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));	
+}
+
+b8 RGFW_isPressed(RGFW_window* win, u8 key) {
+	return RGFW_keyboard[key].current && (win == NULL || win->event.inFocus);
+}
+
+b8 RGFW_wasPressed(RGFW_window* win, u8 key) {
+	return RGFW_keyboard[key].prev && (win == NULL || win->event.inFocus);
+}
+
+b8 RGFW_isHeld(RGFW_window* win, u8 key) {
+	return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));
+}
+
+b8 RGFW_isClicked(RGFW_window* win, u8 key) {
+	return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key));
+}
+
+b8 RGFW_isReleased(RGFW_window* win, u8 key) {
+	return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));	
+}
+
+#if defined(RGFW_WINDOWS)  && defined(RGFW_DIRECTX) /* defines for directX context*/
+	RGFW_directXinfo RGFW_dxInfo;
+	RGFW_directXinfo* RGFW_getDirectXInfo(void) { return &RGFW_dxInfo; }
+#endif
+
+void RGFW_window_makeCurrent(RGFW_window* win) {
+#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX)
+	if (win == NULL)
+		RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, NULL, NULL);
+	else
+		RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL);
+#elif defined(RGFW_OPENGL)
+	RGFW_window_makeCurrent_OpenGL(win);
+#else
+	RGFW_UNUSED(win)
+#endif
+}
+
+void RGFW_window_setGPURender(RGFW_window* win, i8 set) {
+	if (!set && !(win->_winArgs & RGFW_NO_GPU_RENDER))
+		win->_winArgs |= RGFW_NO_GPU_RENDER;
+		
+	else if (set && win->_winArgs & RGFW_NO_GPU_RENDER)
+		win->_winArgs ^= RGFW_NO_GPU_RENDER;
+}
+
+void RGFW_window_setCPURender(RGFW_window* win, i8 set) {
+	if (!set && !(win->_winArgs & RGFW_NO_CPU_RENDER))
+		win->_winArgs |= RGFW_NO_CPU_RENDER;
+
+	else if (set && win->_winArgs & RGFW_NO_CPU_RENDER)
+		win->_winArgs ^= RGFW_NO_CPU_RENDER;
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+	assert(win != NULL);
+
+	RGFW_area screen = RGFW_getScreenSize();
+
+	RGFW_window_move(win, RGFW_POINT(0, 0));
+	RGFW_window_resize(win, screen);
+}
+
+b8 RGFW_window_shouldClose(RGFW_window* win) {
+	assert(win != NULL);
+	return (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape));
+}
+
+void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; RGFW_windowQuitCallback(win); }
+
+#ifndef RGFW_NO_MONITOR
+	void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) {
+		RGFW_window_move(win, RGFW_POINT(m.rect.x + win->r.x, m.rect.y + win->r.y));
+	}
+#endif
+
+RGFWDEF void RGFW_captureCursor(RGFW_window* win, RGFW_rect);
+RGFWDEF void RGFW_releaseCursor(RGFW_window* win);
+
+void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) {
+	if ((win->_winArgs & RGFW_HOLD_MOUSE))
+		return;
+	
+
+	if (!area.w && !area.h)
+		area = RGFW_AREA(win->r.w / 2, win->r.h / 2);
+		
+	win->_winArgs |= RGFW_HOLD_MOUSE;
+	RGFW_captureCursor(win, win->r);
+	RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
+}
+
+void RGFW_window_mouseUnhold(RGFW_window* win) {
+	if ((win->_winArgs & RGFW_HOLD_MOUSE)) {
+		win->_winArgs ^= RGFW_HOLD_MOUSE;
+
+		RGFW_releaseCursor(win);
+	}
+}
+
+u32 RGFW_window_checkFPS(RGFW_window* win, u32 fpsCap) {
+	u64 deltaTime = RGFW_getTimeNS() - win->event.frameTime;
+
+	u32 output_fps = 0;
+	u64 fps = round(1e+9 / deltaTime);
+	output_fps= fps;
+
+	if (fpsCap && fps > fpsCap) {
+		u64 frameTimeNS = 1e+9 / fpsCap;
+		u64 sleepTimeMS = (frameTimeNS - deltaTime) / 1e6;
+
+		if (sleepTimeMS > 0) {
+			RGFW_sleep(sleepTimeMS);
+			win->event.frameTime = 0;
+		}
+	}
+
+	win->event.frameTime = RGFW_getTimeNS();
+	
+	if (fpsCap == 0) 
+		return (u32) output_fps;
+	
+	deltaTime = RGFW_getTimeNS() - win->event.frameTime2;
+	output_fps = round(1e+9 / deltaTime);
+	win->event.frameTime2 = RGFW_getTimeNS();
+
+	return output_fps;
+}
+
+u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { 
+	RGFW_UNUSED(win);
+	return RGFW_jsPressed[c][button]; 
+}
+
+#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
+	void RGFW_window_showMouse(RGFW_window* win, i8 show) {
+		static u8 RGFW_blk[] = { 0, 0, 0, 0 };
+		if (show == 0)
+			RGFW_window_setMouse(win, RGFW_blk, RGFW_AREA(1, 1), 4);
+		else
+			RGFW_window_setMouseDefault(win);
+	}
+#endif
+
+RGFWDEF void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock);	
+void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) {
+	if (capital && !(win->event.lockState & RGFW_CAPSLOCK))
+		win->event.lockState |= RGFW_CAPSLOCK;
+	else if (!capital && (win->event.lockState & RGFW_CAPSLOCK))			
+		win->event.lockState ^= RGFW_CAPSLOCK;
+	
+	if (numlock && !(win->event.lockState & RGFW_NUMLOCK))
+		win->event.lockState |= RGFW_NUMLOCK;
+	else if (!numlock && (win->event.lockState & RGFW_NUMLOCK))
+		win->event.lockState ^= RGFW_NUMLOCK;
+}
+
+#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM) || defined(RGFW_WAYLAND)
+	struct timespec;
+
+	int nanosleep(const struct timespec* duration, struct timespec* rem);
+	int clock_gettime(clockid_t clk_id, struct timespec* tp);
+	int setenv(const char *name, const char *value, int overwrite);
+
+	void RGFW_window_setDND(RGFW_window* win, b8 allow) {
+		if (allow && !(win->_winArgs & RGFW_ALLOW_DND))
+			win->_winArgs |= RGFW_ALLOW_DND;
+
+		else if (!allow && (win->_winArgs & RGFW_ALLOW_DND))
+			win->_winArgs ^= RGFW_ALLOW_DND;
+	}
+#endif
+
+/*
+	graphics API specific code (end of generic code)
+	starts here 
+*/
+
+
+/* 
+	OpenGL defines start here   (Normal, EGL, OSMesa)
+*/
+
+#if defined(RGFW_OPENGL) || defined(RGFW_EGL) || defined(RGFW_OSMESA)
+	#ifdef RGFW_WINDOWS
+		#define WIN32_LEAN_AND_MEAN
+		#define OEMRESOURCE
+		#include <windows.h>
+	#endif
+
+	#if !defined(__APPLE__) && !defined(RGFW_NO_GL_HEADER)
+		#include <GL/gl.h>
+	#elif defined(__APPLE__)
+		#ifndef GL_SILENCE_DEPRECATION
+			#define GL_SILENCE_DEPRECATION
+		#endif
+		#include <OpenGL/gl.h>
+		#include <OpenGL/OpenGL.h>
+	#endif
+
+/* EGL, normal OpenGL only */
+#if !defined(RGFW_OSMESA) 
+	i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0;
+	b8 RGFW_profile = RGFW_GL_CORE;
+	
+	#ifndef RGFW_EGL
+	i32 RGFW_STENCIL = 8, RGFW_SAMPLES = 4, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1;
+	#else
+	i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1;
+	#endif
+
+
+	void RGFW_setGLStencil(i32 stencil) { RGFW_STENCIL = stencil; }
+	void RGFW_setGLSamples(i32 samples) { RGFW_SAMPLES = samples; }
+	void RGFW_setGLStereo(i32 stereo) { RGFW_STEREO = stereo; }
+	void RGFW_setGLAuxBuffers(i32 auxBuffers) { RGFW_AUX_BUFFERS = auxBuffers; }
+	void RGFW_setDoubleBuffer(b8 useDoubleBuffer) { RGFW_DOUBLE_BUFFER = useDoubleBuffer; }
+
+	void RGFW_setGLVersion(b8 profile, i32 major, i32 minor) {
+        RGFW_profile = profile;
+		RGFW_majorVersion = major;
+		RGFW_minorVersion = minor;
+	}
+
+/* OPENGL normal only (no EGL / OSMesa) */
+#ifndef RGFW_EGL
+
+#define RGFW_GL_RENDER_TYPE 		RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE,    	0x2003,		73, 0, 0)
+	#define RGFW_GL_ALPHA_SIZE 		RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE,       	0x201b,		11,     0, 0)
+	#define RGFW_GL_DEPTH_SIZE 		RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE,       	0x2022,		12,     0, 0)
+	#define RGFW_GL_DOUBLEBUFFER 		RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER,     	0x2011, 	5,  0, 0)   
+	#define RGFW_GL_STENCIL_SIZE 		RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE,	 	0x2023,	13,     0, 0)
+	#define RGFW_GL_SAMPLES			RGFW_OS_BASED_VALUE(GLX_SAMPLES, 		 	0x2042,	    55,     0, 0)
+	#define RGFW_GL_STEREO 			RGFW_OS_BASED_VALUE(GLX_STEREO,	 		 	0x2012,			6,  0, 0)
+	#define RGFW_GL_AUX_BUFFERS		RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS,	    0x2024,	7, 		    0, 0)
+
+#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
+	#define RGFW_GL_DRAW 			RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE,	 	0x2001,					0, 0, 0)
+	#define RGFW_GL_DRAW_TYPE 		RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE,     	0x2013,						0, 0, 0)
+	#define RGFW_GL_FULL_FORMAT		RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR,   	 	0x2027,						0, 0, 0)
+	#define RGFW_GL_RED_SIZE		RGFW_OS_BASED_VALUE(GLX_RED_SIZE,         	0x2015,						0, 0, 0)
+	#define RGFW_GL_GREEN_SIZE		RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE,       	0x2017,						0, 0, 0)
+	#define RGFW_GL_BLUE_SIZE		RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 	 		0x2019,						0, 0, 0)
+	#define RGFW_GL_USE_RGBA		RGFW_OS_BASED_VALUE(GLX_RGBA_BIT,   	 	0x202B,						0, 0, 0)
+#endif
+
+#ifdef RGFW_WINDOWS
+	#define WGL_SUPPORT_OPENGL_ARB                    0x2010
+	#define WGL_COLOR_BITS_ARB                        0x2014
+	#define WGL_NUMBER_PIXEL_FORMATS_ARB 			0x2000
+	#define WGL_CONTEXT_MAJOR_VERSION_ARB             0x2091
+	#define WGL_CONTEXT_MINOR_VERSION_ARB             0x2092
+	#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126
+	#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+	#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+	#define WGL_SAMPLE_BUFFERS_ARB               0x2041
+	#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
+	#define WGL_PIXEL_TYPE_ARB                        0x2013
+	#define WGL_TYPE_RGBA_ARB                         0x202B
+
+	#define WGL_TRANSPARENT_ARB   					  0x200A
+#endif
+	
+/*  The window'ing api needs to know how to render the data we (or opengl) give it 
+	MacOS and Windows do this using a structure called a "pixel format" 
+	X11 calls it a "Visual"
+	This function returns the attributes for the format we want */
+	static u32* RGFW_initFormatAttribs(u32 useSoftware) {
+		RGFW_UNUSED(useSoftware);
+		static u32 attribs[] = {
+								#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
+								RGFW_GL_RENDER_TYPE,
+								RGFW_GL_FULL_FORMAT,
+								#endif
+								RGFW_GL_ALPHA_SIZE      , 8,
+								RGFW_GL_DEPTH_SIZE      , 24,
+								#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
+								RGFW_GL_DRAW, 1,
+								RGFW_GL_RED_SIZE        , 8,
+								RGFW_GL_GREEN_SIZE      , 8,
+								RGFW_GL_BLUE_SIZE       , 8,
+								RGFW_GL_DRAW_TYPE     , RGFW_GL_USE_RGBA,
+								#endif 
+
+								#ifdef RGFW_X11
+								GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
+								#endif	
+
+								#ifdef RGFW_MACOS
+								72,
+								8, 24,
+								#endif
+
+								#ifdef RGFW_WINDOWS
+								WGL_SUPPORT_OPENGL_ARB,		1,
+								WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
+								WGL_COLOR_BITS_ARB,	 32,
+								#endif
+
+								0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+		};
+
+		size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13;
+
+#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
+		if (attVal) { \
+			attribs[index] = attrib;\
+			attribs[index + 1] = attVal;\
+			index += 2;\
+		}
+         
+        RGFW_GL_ADD_ATTRIB(RGFW_GL_DOUBLEBUFFER, 1);
+        
+        RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_STENCIL);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_STEREO);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_AUX_BUFFERS);
+
+#ifndef RGFW_X11
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_SAMPLES);
+#endif 
+
+#ifdef RGFW_MACOS
+		if (useSoftware) {
+			RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID);
+		} else {
+			attribs[index] = RGFW_GL_RENDER_TYPE;
+			index += 1;
+		}
+#endif
+
+#ifdef RGFW_MACOS
+		/* macOS has the surface attribs and the opengl attribs connected for some reason
+			maybe this is to give macOS more control to limit openGL/the opengl version? */
+
+		attribs[index] = 99;
+		attribs[index + 1] = 0x1000;
+
+		if (RGFW_majorVersion >= 4 || RGFW_majorVersion >= 3) {
+			attribs[index + 1] = (u32) ((RGFW_majorVersion >= 4) ? 0x4100 : 0x3200);
+		}
+#endif
+
+		RGFW_GL_ADD_ATTRIB(0, 0);
+
+		return attribs;
+	}
+
+/* EGL only (no OSMesa nor normal OPENGL) */
+#elif defined(RGFW_EGL)
+
+#include <EGL/egl.h>
+
+#if defined(RGFW_LINK_EGL)
+	typedef EGLBoolean(EGLAPIENTRY* PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*);
+
+	PFNEGLINITIALIZEPROC eglInitializeSource;
+	PFNEGLGETCONFIGSPROC eglGetConfigsSource;
+	PFNEGLCHOOSECONFIGPROC eglChooseConfigSource;
+	PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceSource;
+	PFNEGLCREATECONTEXTPROC eglCreateContextSource;
+	PFNEGLMAKECURRENTPROC eglMakeCurrentSource;
+	PFNEGLGETDISPLAYPROC eglGetDisplaySource;
+	PFNEGLSWAPBUFFERSPROC eglSwapBuffersSource;
+	PFNEGLSWAPINTERVALPROC eglSwapIntervalSource;
+	PFNEGLBINDAPIPROC eglBindAPISource;
+	PFNEGLDESTROYCONTEXTPROC eglDestroyContextSource;
+	PFNEGLTERMINATEPROC eglTerminateSource;
+	PFNEGLDESTROYSURFACEPROC eglDestroySurfaceSource;
+
+#define eglInitialize eglInitializeSource
+#define eglGetConfigs eglGetConfigsSource
+#define eglChooseConfig eglChooseConfigSource
+#define eglCreateWindowSurface eglCreateWindowSurfaceSource
+#define eglCreateContext eglCreateContextSource
+#define eglMakeCurrent eglMakeCurrentSource
+#define eglGetDisplay eglGetDisplaySource
+#define eglSwapBuffers eglSwapBuffersSource
+#define eglSwapInterval eglSwapIntervalSource
+#define eglBindAPI eglBindAPISource
+#define eglDestroyContext eglDestroyContextSource
+#define eglTerminate eglTerminateSource
+#define eglDestroySurface eglDestroySurfaceSource;
+#endif
+
+
+#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098
+#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb
+
+#ifndef RGFW_GL_ADD_ATTRIB
+#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
+	if (attVal) { \
+		attribs[index] = attrib;\
+		attribs[index + 1] = attVal;\
+		index += 2;\
+	}
+#endif
+
+
+	void RGFW_createOpenGLContext(RGFW_window* win) {
+#if defined(RGFW_LINK_EGL)
+		eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize");
+		eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs");
+		eglChooseConfigSource = (PFNEGLCHOOSECONFIGPROC) eglGetProcAddress("eglChooseConfig");
+		eglCreateWindowSurfaceSource = (PFNEGLCREATEWINDOWSURFACEPROC) eglGetProcAddress("eglCreateWindowSurface");
+		eglCreateContextSource = (PFNEGLCREATECONTEXTPROC) eglGetProcAddress("eglCreateContext");
+		eglMakeCurrentSource = (PFNEGLMAKECURRENTPROC) eglGetProcAddress("eglMakeCurrent");
+		eglGetDisplaySource = (PFNEGLGETDISPLAYPROC) eglGetProcAddress("eglGetDisplay");
+		eglSwapBuffersSource = (PFNEGLSWAPBUFFERSPROC) eglGetProcAddress("eglSwapBuffers");
+		eglSwapIntervalSource = (PFNEGLSWAPINTERVALPROC) eglGetProcAddress("eglSwapInterval");
+		eglBindAPISource = (PFNEGLBINDAPIPROC) eglGetProcAddress("eglBindAPI");
+		eglDestroyContextSource = (PFNEGLDESTROYCONTEXTPROC) eglGetProcAddress("eglDestroyContext");
+		eglTerminateSource = (PFNEGLTERMINATEPROC) eglGetProcAddress("eglTerminate");
+		eglDestroySurfaceSource = (PFNEGLDESTROYSURFACEPROC) eglGetProcAddress("eglDestroySurface");
+#endif /* RGFW_LINK_EGL */
+
+		#ifdef RGFW_WINDOWS
+		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.hdc);
+		#elif defined(RGFW_MACOS)
+		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType)0);
+		#else
+		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display);
+		#endif
+
+		EGLint major, minor;
+
+		eglInitialize(win->src.EGL_display, &major, &minor);
+
+		#ifndef EGL_OPENGL_ES1_BIT
+		#define EGL_OPENGL_ES1_BIT 0x1
+		#endif
+
+		EGLint egl_config[] = {
+			EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+			EGL_RENDERABLE_TYPE,
+			#ifdef RGFW_OPENGL_ES1
+			EGL_OPENGL_ES1_BIT,
+			#elif defined(RGFW_OPENGL_ES3)
+			EGL_OPENGL_ES3_BIT,
+			#elif defined(RGFW_OPENGL_ES2)
+			EGL_OPENGL_ES2_BIT,
+			#else
+			EGL_OPENGL_BIT,
+			#endif
+			EGL_NONE, EGL_NONE
+		};
+
+		EGLConfig config;
+		EGLint numConfigs;
+		eglChooseConfig(win->src.EGL_display, egl_config, &config, 1, &numConfigs);
+
+		#if defined(RGFW_MACOS)
+		    void* layer = RGFW_cocoaGetLayer(); 
+		
+			RGFW_window_cocoaSetLayer(win, layer);
+			
+			win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) layer, NULL);
+		#else
+			win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL);
+		#endif
+
+		EGLint attribs[] = {
+			EGL_CONTEXT_CLIENT_VERSION,
+			#ifdef RGFW_OPENGL_ES1
+			1,
+			#else
+			2,
+			#endif
+			EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE
+		};
+
+		size_t index = 4;
+		RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_STENCIL);
+		RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_SAMPLES);
+
+        if (RGFW_DOUBLE_BUFFER)
+            RGFW_GL_ADD_ATTRIB(EGL_RENDER_BUFFER, EGL_BACK_BUFFER);
+
+		if (RGFW_majorVersion) {
+			attribs[1] = RGFW_majorVersion;
+	
+			RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_majorVersion);
+			RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_minorVersion);
+
+			if (RGFW_profile == RGFW_GL_CORE) {
+				RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
+			}
+			else {
+				RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
+			}
+
+		}
+
+		#if defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)
+		eglBindAPI(EGL_OPENGL_ES_API);
+		#else
+		eglBindAPI(EGL_OPENGL_API);		
+		#endif
+      		
+		win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs);
+		
+		if (win->src.EGL_context == NULL)
+			fprintf(stderr, "failed to create an EGL opengl context\n");
+
+		eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context);
+		eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
+	}
+
+	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+		eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context);
+	}
+
+	#ifdef RGFW_APPLE
+	void* RGFWnsglFramework = NULL;
+	#elif defined(RGFW_WINDOWS)
+	static HMODULE wglinstance = NULL;
+	#endif
+
+	void* RGFW_getProcAddress(const char* procname) { 
+		#if defined(RGFW_WINDOWS)
+			void* proc = (void*) GetProcAddress(wglinstance, procname); 
+
+			if (proc)
+				return proc;
+		#endif
+
+		return (void*) eglGetProcAddress(procname); 
+	}
+
+	void RGFW_closeEGL(RGFW_window* win) {
+		eglDestroySurface(win->src.EGL_display, win->src.EGL_surface);
+		eglDestroyContext(win->src.EGL_display, win->src.EGL_context);
+
+		eglTerminate(win->src.EGL_display);
+	}
+	
+	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
+		assert(win != NULL);
+		
+		eglSwapInterval(win->src.EGL_display, swapInterval);
+
+	}
+#endif /* RGFW_EGL */
+
+/* 
+	end of RGFW_EGL defines
+*/
+
+/* OPENGL Normal / EGL defines only (no OS MESA)  Ends here */
+
+#elif defined(RGFW_OSMESA) /* OSmesa only */
+RGFWDEF void RGFW_OSMesa_reorganize(void);
+
+/* reorganize buffer for osmesa */
+void RGFW_OSMesa_reorganize(void) {
+	u8* row = (u8*) RGFW_MALLOC(win->r.w * 3);
+
+	i32 half_height = win->r.h / 2;
+	i32 stride = win->r.w * 3;
+
+	i32 y;
+	for (y = 0; y < half_height; ++y) {
+		i32 top_offset = y * stride;
+		i32 bottom_offset = (win->r.h - y - 1) * stride;
+		memcpy(row, win->buffer + top_offset, stride);
+		memcpy(win->buffer + top_offset, win->buffer + bottom_offset, stride);
+		memcpy(win->buffer + bottom_offset, row, stride);
+	}
+
+	RGFW_FREE(row);
+}
+#endif /* RGFW_OSMesa */
+
+#endif /* RGFW_GL (OpenGL, EGL, OSMesa )*/
+
+/*
+This is where OS specific stuff starts
+*/
+
+
+#if defined(RGFW_WAYLAND) || defined(RGFW_X11)
+	int RGFW_eventWait_forceStop[] = {0, 0, 0}; /* for wait events */
+
+	#ifdef __linux__
+		#include <linux/joystick.h>
+		#include <fcntl.h>
+		#include <unistd.h>
+		
+		RGFW_Event* RGFW_linux_updateJoystick(RGFW_window* win) {
+			static int xAxis = 0, yAxis = 0;
+			u8 i;
+			for (i = 0; i < RGFW_joystickCount; i++) {
+				struct js_event e;
+
+
+				if (RGFW_joysticks[i] == 0)
+					continue;
+
+				i32 flags = fcntl(RGFW_joysticks[i], F_GETFL, 0);
+				fcntl(RGFW_joysticks[i], F_SETFL, flags | O_NONBLOCK);
+
+				ssize_t bytes;
+				while ((bytes = read(RGFW_joysticks[i], &e, sizeof(e))) > 0) {
+					switch (e.type) {
+					case JS_EVENT_BUTTON:
+						win->event.type = e.value ? RGFW_jsButtonPressed : RGFW_jsButtonReleased;
+						win->event.button = e.number;
+						RGFW_jsPressed[i][e.number] = e.value;
+						RGFW_jsButtonCallback(win, i, e.number, e.value);
+						return &win->event;
+					case JS_EVENT_AXIS:
+						ioctl(RGFW_joysticks[i], JSIOCGAXES, &win->event.axisesCount);
+
+						if ((e.number == 0 || e.number % 2) && e.number != 1)
+							xAxis = e.value;
+						else
+							yAxis = e.value;
+
+						win->event.axis[e.number / 2].x = xAxis;
+						win->event.axis[e.number / 2].y = yAxis;
+						win->event.type = RGFW_jsAxisMove;
+						win->event.joystick = i;
+						RGFW_jsAxisCallback(win, i, win->event.axis, win->event.axisesCount);
+						return &win->event;
+
+						default: break;
+					}
+				}
+			}
+
+			return NULL;
+		}
+
+	#endif
+#endif
+
+/*
+
+
+Start of Linux / Unix defines
+
+
+*/
+
+#ifdef RGFW_X11
+#ifndef RGFW_NO_X11_CURSOR
+#include <X11/Xcursor/Xcursor.h>
+#endif
+#include <dlfcn.h>
+
+#ifndef RGFW_NO_DPI
+#include <X11/extensions/Xrandr.h>
+#include <X11/Xresource.h>
+#endif
+
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/keysymdef.h>
+#include <unistd.h>
+
+#include <X11/XKBlib.h> /* for converting keycode to string */
+#include <X11/cursorfont.h> /* for hiding */
+#include <X11/extensions/shapeconst.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/XInput2.h>
+
+#include <limits.h> /* for data limits (mainly used in drag and drop functions) */
+#include <poll.h>
+
+
+#ifdef __linux__
+#include <linux/joystick.h>
+#endif
+
+	u8 RGFW_mouseIconSrc[] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor};  
+	/*atoms needed for drag and drop*/
+	Atom XdndAware, XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XtextPlain, XtextUriList;
+
+	Atom wm_delete_window = 0;
+
+#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+	typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int);
+	typedef void (*PFN_XcursorImageDestroy)(XcursorImage*);
+	typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*);
+#endif
+#ifdef RGFW_OPENGL
+	typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
+#endif
+
+#if !defined(RGFW_NO_X11_XI_PRELOAD)
+	typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
+	PFN_XISelectEvents XISelectEventsSrc = NULL;
+	#define XISelectEvents XISelectEventsSrc
+
+	void* X11Xihandle = NULL;
+#endif
+
+#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+	PFN_XcursorImageLoadCursor XcursorImageLoadCursorSrc = NULL;
+	PFN_XcursorImageCreate XcursorImageCreateSrc = NULL;
+	PFN_XcursorImageDestroy XcursorImageDestroySrc = NULL;
+
+#define XcursorImageLoadCursor XcursorImageLoadCursorSrc
+#define XcursorImageCreate XcursorImageCreateSrc
+#define XcursorImageDestroy XcursorImageDestroySrc
+
+	void* X11Cursorhandle = NULL;
+#endif
+
+	u32 RGFW_windowsOpen = 0;
+
+#ifdef RGFW_OPENGL
+	void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); }
+#endif
+
+	RGFWDEF void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi);
+	void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi) {
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+		if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
+			RGFW_bufferSize = RGFW_getScreenSize();
+		
+		win->buffer = (u8*)RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4);
+
+		#ifdef RGFW_OSMESA
+				win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
+				OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
+		#endif
+
+		win->src.bitmap = XCreateImage(
+			win->src.display, XDefaultVisual(win->src.display, vi->screen),
+			vi->depth,
+			ZPixmap, 0, NULL, RGFW_bufferSize.w, RGFW_bufferSize.h,
+			32, 0
+		);
+
+		win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL);
+
+		#else
+		RGFW_UNUSED(win); /*!< if buffer rendering is not being used */
+		RGFW_UNUSED(vi)
+		#endif
+	}
+
+
+
+	void RGFW_window_setBorder(RGFW_window* win, u8 border) {
+		static Atom _MOTIF_WM_HINTS = 0;
+		if (_MOTIF_WM_HINTS == 0 )
+			_MOTIF_WM_HINTS = XInternAtom(win->src.display, "_MOTIF_WM_HINTS", False);
+		
+		struct __x11WindowHints {
+			unsigned long flags, functions, decorations, status;
+			long input_mode;
+		} hints;
+		hints.flags = (1L << 1);
+		hints.decorations = border;
+
+		XChangeProperty(
+			win->src.display, win->src.window,
+			_MOTIF_WM_HINTS, _MOTIF_WM_HINTS,
+			32, PropModeReplace, (u8*)&hints, 5
+		);
+	}
+	
+	void RGFW_releaseCursor(RGFW_window* win) {
+		XUngrabPointer(win->src.display, CurrentTime);
+
+		/* disable raw input */
+		unsigned char mask[] = { 0 };
+		XIEventMask em;
+		em.deviceid = XIAllMasterDevices;
+		em.mask_len = sizeof(mask);
+		em.mask = mask;
+
+		XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1);
+	}
+	
+	void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { 
+		/* enable raw input */
+		unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
+		XISetMask(mask, XI_RawMotion);
+
+		XIEventMask em;
+		em.deviceid = XIAllMasterDevices;
+		em.mask_len = sizeof(mask);
+		em.mask = mask;
+		
+		XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1);
+
+		XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
+
+		RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2)));
+	}
+
+	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
+#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+		if (X11Cursorhandle == NULL) {
+#if defined(__CYGWIN__)
+			X11Cursorhandle = dlopen("libXcursor-1.so", RTLD_LAZY | RTLD_LOCAL);
+#elif defined(__OpenBSD__) || defined(__NetBSD__)
+			X11Cursorhandle = dlopen("libXcursor.so", RTLD_LAZY | RTLD_LOCAL);
+#else
+			X11Cursorhandle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_LOCAL);
+#endif
+
+			XcursorImageCreateSrc = (PFN_XcursorImageCreate) dlsym(X11Cursorhandle, "XcursorImageCreate");
+			XcursorImageDestroySrc = (PFN_XcursorImageDestroy) dlsym(X11Cursorhandle, "XcursorImageDestroy");
+			XcursorImageLoadCursorSrc = (PFN_XcursorImageLoadCursor) dlsym(X11Cursorhandle, "XcursorImageLoadCursor");
+		}
+#endif
+
+#if !defined(RGFW_NO_X11_XI_PRELOAD)
+		if (X11Xihandle == NULL) {
+#if defined(__CYGWIN__)
+			X11Xihandle = dlopen("libXi-6.so", RTLD_LAZY | RTLD_LOCAL);
+#elif defined(__OpenBSD__) || defined(__NetBSD__)
+			X11Xihandle = dlopen("libXi.so", RTLD_LAZY | RTLD_LOCAL);
+#else
+			X11Xihandle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_LOCAL);
+#endif
+
+			XISelectEventsSrc = (PFN_XISelectEvents) dlsym(X11Xihandle, "XISelectEvents");
+		}
+#endif
+
+		XInitThreads(); /*!< init X11 threading*/
+
+		if (args & RGFW_OPENGL_SOFTWARE)
+			setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
+
+		RGFW_window* win = RGFW_window_basic_init(rect, args);
+
+		u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted*/
+
+#ifdef RGFW_OPENGL
+		u32* visual_attribs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE);
+		i32 fbcount;
+		GLXFBConfig* fbc = glXChooseFBConfig((Display*) win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount);
+
+		i32 best_fbc = -1;
+
+		if (fbcount == 0) {
+			printf("Failed to find any valid GLX visual configs\n");
+			return NULL;
+		}
+
+		u32 i;
+		for (i = 0; i < (u32)fbcount; i++) {
+			XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, fbc[i]);
+                        if (vi == NULL)
+				continue;
+                        
+			XFree(vi);
+
+			i32 samp_buf, samples;
+			glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+			glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLES, &samples);
+			
+			if ((best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES || best_fbc == -1)) {
+				best_fbc = i;
+			}
+		}
+
+		if (best_fbc == -1) {
+			printf("Failed to get a valid GLX visual\n");
+			return NULL;
+		}
+
+		GLXFBConfig bestFbc = fbc[best_fbc];
+
+		/* Get a visual */
+		XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, bestFbc);
+		
+		XFree(fbc);
+		
+		if (args & RGFW_TRANSPARENT_WINDOW) {
+			XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/
+		}
+		
+#else
+		XVisualInfo viNorm;
+
+		viNorm.visual = DefaultVisual((Display*) win->src.display, DefaultScreen((Display*) win->src.display));
+		
+		viNorm.depth = 0;
+		XVisualInfo* vi = &viNorm;
+		
+		XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/
+#endif
+		/* make X window attrubutes*/
+		XSetWindowAttributes swa;
+		Colormap cmap;
+
+		swa.colormap = cmap = XCreateColormap((Display*) win->src.display,
+			DefaultRootWindow(win->src.display),
+			vi->visual, AllocNone);
+
+		swa.background_pixmap = None;
+		swa.border_pixel = 0;
+		swa.event_mask = event_mask;
+		
+		swa.background_pixel = 0;
+
+		/* create the window*/
+		win->src.window = XCreateWindow((Display*) win->src.display, DefaultRootWindow((Display*) win->src.display), win->r.x, win->r.y, win->r.w, win->r.h,
+			0, vi->depth, InputOutput, vi->visual,
+			CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa);
+
+		XFreeColors((Display*) win->src.display, cmap, NULL, 0, 0);
+
+		#ifdef RGFW_OPENGL
+		XFree(vi);
+		#endif
+
+		// In your .desktop app, if you set the property
+		// StartupWMClass=RGFW that will assoicate the launcher icon
+		// with your application - robrohan 
+		
+		if (RGFW_className == NULL)
+			RGFW_className = (char*)name;
+
+		XClassHint *hint = XAllocClassHint();
+		assert(hint != NULL);
+		hint->res_class = (char*)RGFW_className;
+		hint->res_name = (char*)name; // just use the window name as the app name
+		XSetClassHint((Display*) win->src.display, win->src.window, hint);
+		XFree(hint);
+
+		if ((args & RGFW_NO_INIT_API) == 0) {
+#ifdef RGFW_OPENGL /* This is the second part of setting up opengl. This is where we ask OpenGL for a specific version. */ 
+		i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 };
+		context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB;
+		if (RGFW_profile == RGFW_GL_CORE) 
+			context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
+		else 
+			context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
+		
+		if (RGFW_majorVersion || RGFW_minorVersion) {
+			context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB;
+			context_attribs[3] = RGFW_majorVersion;
+			context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB;
+			context_attribs[5] = RGFW_minorVersion;
+		}
+
+		glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
+		glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
+			glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB");
+
+		GLXContext ctx = NULL;
+
+		if (RGFW_root != NULL)
+			ctx = RGFW_root->src.ctx;
+
+		win->src.ctx = glXCreateContextAttribsARB((Display*) win->src.display, bestFbc, ctx, True, context_attribs);
+#endif
+		if (RGFW_root == NULL)
+			RGFW_root = win;
+
+		RGFW_init_buffer(win, vi);
+		}
+		
+
+		#ifndef RGFW_NO_MONITOR
+		if (args & RGFW_SCALE_TO_MONITOR)
+			RGFW_window_scaleToMonitor(win);
+		#endif
+
+		if (args & RGFW_CENTER) {
+			RGFW_area screenR = RGFW_getScreenSize();
+			RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2));
+		}
+
+		if (args & RGFW_NO_RESIZE) { /* make it so the user can't resize the window*/
+			XSizeHints* sh = XAllocSizeHints();
+			sh->flags = (1L << 4) | (1L << 5);
+			sh->min_width = sh->max_width = win->r.w;
+			sh->min_height = sh->max_height = win->r.h;
+
+			XSetWMSizeHints((Display*) win->src.display, (Drawable) win->src.window, sh, XA_WM_NORMAL_HINTS);
+			XFree(sh);
+		}
+
+		if (args & RGFW_NO_BORDER) {
+			RGFW_window_setBorder(win, 0);
+		}
+
+		XSelectInput((Display*) win->src.display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want*/
+
+		/* make it so the user can't close the window until the program does*/
+		if (wm_delete_window == 0)
+			wm_delete_window = XInternAtom((Display*) win->src.display, "WM_DELETE_WINDOW", False);
+
+		XSetWMProtocols((Display*) win->src.display, (Drawable) win->src.window, &wm_delete_window, 1);
+
+		/* connect the context to the window*/
+#ifdef RGFW_OPENGL
+		if ((args & RGFW_NO_INIT_API) == 0)
+			glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx);
+#endif
+
+		/* set the background*/
+		XStoreName((Display*) win->src.display, (Drawable) win->src.window, name); /*!< set the name*/
+
+		XMapWindow((Display*) win->src.display, (Drawable) win->src.window);						  /* draw the window*/
+		XMoveWindow((Display*) win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords*/
+
+		if (args & RGFW_ALLOW_DND) { /* init drag and drop atoms and turn on drag and drop for this window */
+			win->_winArgs |= RGFW_ALLOW_DND;
+
+			XdndTypeList = XInternAtom((Display*) win->src.display, "XdndTypeList", False);
+			XdndSelection = XInternAtom((Display*) win->src.display, "XdndSelection", False);
+
+			/* client messages */
+			XdndEnter = XInternAtom((Display*) win->src.display, "XdndEnter", False);
+			XdndPosition = XInternAtom((Display*) win->src.display, "XdndPosition", False);
+			XdndStatus = XInternAtom((Display*) win->src.display, "XdndStatus", False);
+			XdndLeave = XInternAtom((Display*) win->src.display, "XdndLeave", False);
+			XdndDrop = XInternAtom((Display*) win->src.display, "XdndDrop", False);
+			XdndFinished = XInternAtom((Display*) win->src.display, "XdndFinished", False);
+
+			/* actions */
+			XdndActionCopy = XInternAtom((Display*) win->src.display, "XdndActionCopy", False);
+
+			XtextUriList = XInternAtom((Display*) win->src.display, "text/uri-list", False); 
+			XtextPlain = XInternAtom((Display*) win->src.display, "text/plain", False);
+
+			XdndAware = XInternAtom((Display*) win->src.display, "XdndAware", False);
+			const u8 version = 5;
+
+			XChangeProperty((Display*) win->src.display, (Window) win->src.window,
+				XdndAware, 4, 32,
+				PropModeReplace, &version, 1); /*!< turns on drag and drop */
+		}
+
+		#ifdef RGFW_EGL
+			if ((args & RGFW_NO_INIT_API) == 0)
+				RGFW_createOpenGLContext(win);
+		#endif
+
+		RGFW_window_setMouseDefault(win);
+
+		RGFW_windowsOpen++;
+
+		return win; /*return newly created window*/
+	}
+
+	RGFW_area RGFW_getScreenSize(void) {
+		assert(RGFW_root != NULL);
+
+		Screen* scrn = DefaultScreenOfDisplay((Display*) RGFW_root->src.display);
+		return RGFW_AREA(scrn->width, scrn->height);
+	}
+
+	RGFW_point RGFW_getGlobalMousePoint(void) {
+		assert(RGFW_root != NULL);
+
+		RGFW_point RGFWMouse;
+
+		i32 x, y;
+		u32 z;
+		Window window1, window2;
+		XQueryPointer((Display*) RGFW_root->src.display, XDefaultRootWindow((Display*) RGFW_root->src.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z);
+ 
+		return RGFWMouse;
+	}
+
+	RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
+		assert(win != NULL);
+
+		RGFW_point RGFWMouse;
+
+		i32 x, y;
+		u32 z;
+		Window window1, window2;
+		XQueryPointer((Display*) win->src.display, win->src.window, &window1, &window2, &x, &y, &RGFWMouse.x, &RGFWMouse.y, &z);
+
+		return RGFWMouse;
+	}
+
+	int xAxis = 0, yAxis = 0;
+
+	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
+		assert(win != NULL);
+
+		static struct {
+			long source, version;
+			i32 format;
+		} xdnd;
+
+		if (win->event.type == 0) 
+			RGFW_resetKey();
+
+		if (win->event.type == RGFW_quit) {
+			return NULL;
+		}
+
+		win->event.type = 0;
+
+#ifdef __linux__
+	RGFW_Event* event = RGFW_linux_updateJoystick(win);
+	if (event != NULL)
+		return event;
+#endif
+
+		XPending(win->src.display);
+
+		XEvent E; /*!< raw X11 event */
+
+		/* if there is no unread qued events, get a new one */
+		if ((QLength(win->src.display) || XEventsQueued((Display*) win->src.display, QueuedAlready) + XEventsQueued((Display*) win->src.display, QueuedAfterReading)) 
+			&& win->event.type != RGFW_quit
+		)
+			XNextEvent((Display*) win->src.display, &E);
+		else {
+			return NULL;
+		}
+
+		u32 i;
+		win->event.type = 0;
+
+
+		switch (E.type) {
+		case KeyPress:
+		case KeyRelease: {
+			win->event.repeat = RGFW_FALSE;
+			/* check if it's a real key release */
+			if (E.type == KeyRelease && XEventsQueued((Display*) win->src.display, QueuedAfterReading)) { /* get next event if there is one*/
+				XEvent NE;
+				XPeekEvent((Display*) win->src.display, &NE);
+
+				if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same*/
+					win->event.repeat = RGFW_TRUE;
+			}
+
+			/* set event key data */
+			KeySym sym = (KeySym)XkbKeycodeToKeysym((Display*) win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0);
+			win->event.keyCode = RGFW_apiKeyCodeToRGFW(E.xkey.keycode);
+			
+			char* str = (char*)XKeysymToString(sym);
+			if (str != NULL)
+				strncpy(win->event.keyName, str, 16);
+
+			win->event.keyName[15] = '\0';		
+
+			RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode);
+			
+			/* get keystate data */
+			win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased;
+
+			XKeyboardState keystate;
+			XGetKeyboardControl((Display*) win->src.display, &keystate);
+
+			RGFW_updateLockState(win, (keystate.led_mask & 1), (keystate.led_mask & 2));
+			RGFW_keyboard[win->event.keyCode].current = (E.type == KeyPress);
+			RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, (E.type == KeyPress));
+			break;
+		}
+		case ButtonPress:
+		case ButtonRelease:
+			win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); // the events match 
+			
+			switch(win->event.button) {
+				case RGFW_mouseScrollUp:
+					win->event.scroll = 1;
+					break;
+				case RGFW_mouseScrollDown:
+					win->event.scroll = -1;
+					break;
+				default: break;
+			}
+
+			win->event.button = E.xbutton.button;
+			RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+
+			if (win->event.repeat == RGFW_FALSE)
+				win->event.repeat = RGFW_isPressed(win, win->event.keyCode);
+
+			RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress);
+			RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress));
+			break;
+
+		case MotionNotify:	
+			win->event.point.x = E.xmotion.x;
+			win->event.point.y = E.xmotion.y;
+			
+			if ((win->_winArgs & RGFW_HOLD_MOUSE)) {
+				win->event.point.y = E.xmotion.y;
+
+				win->event.point.x = win->_lastMousePoint.x - abs(win->event.point.x);
+				win->event.point.y = win->_lastMousePoint.y - abs(win->event.point.y);
+			}
+
+			win->_lastMousePoint = RGFW_POINT(E.xmotion.x, E.xmotion.y);
+
+			win->event.type = RGFW_mousePosChanged;
+			RGFW_mousePosCallback(win, win->event.point);
+			break;
+
+		case GenericEvent: {
+			/* MotionNotify is used for mouse events if the mouse isn't held */                
+			if (!(win->_winArgs & RGFW_HOLD_MOUSE)) {
+            	XFreeEventData(win->src.display, &E.xcookie);
+				break;
+			}
+			
+            XGetEventData(win->src.display, &E.xcookie);
+            if (E.xcookie.evtype == XI_RawMotion) {
+				XIRawEvent *raw = (XIRawEvent *)E.xcookie.data;
+				if (raw->valuators.mask_len == 0) {
+					XFreeEventData(win->src.display, &E.xcookie);
+					break;
+				}
+
+                double deltaX = 0.0f; 
+				double deltaY = 0.0f;
+
+                /* check if relative motion data exists where we think it does */
+				if (XIMaskIsSet(raw->valuators.mask, 0) != 0)
+					deltaX += raw->raw_values[0];
+				if (XIMaskIsSet(raw->valuators.mask, 1) != 0)
+					deltaY += raw->raw_values[1];
+
+				win->event.point = RGFW_POINT((i32)deltaX, (i32)deltaY);
+				
+				RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
+
+				win->event.type = RGFW_mousePosChanged;
+				RGFW_mousePosCallback(win, win->event.point);
+            }
+
+            XFreeEventData(win->src.display, &E.xcookie);
+			break;
+		}
+		
+		case Expose:
+			win->event.type = RGFW_windowRefresh;
+			RGFW_windowRefreshCallback(win);
+			break;
+
+		case ClientMessage:
+			/* if the client closed the window*/
+			if (E.xclient.data.l[0] == (i64) wm_delete_window) {
+				win->event.type = RGFW_quit;
+				RGFW_windowQuitCallback(win);
+				break;
+			}
+			
+			/* reset DND values */
+			if (win->event.droppedFilesCount) {
+				for (i = 0; i < win->event.droppedFilesCount; i++)
+					win->event.droppedFiles[i][0] = '\0';
+			}
+
+			win->event.droppedFilesCount = 0;
+
+			if ((win->_winArgs & RGFW_ALLOW_DND) == 0)
+				break;
+
+			XEvent reply = { ClientMessage };
+			reply.xclient.window = xdnd.source;
+			reply.xclient.format = 32;
+			reply.xclient.data.l[0] = (long) win->src.window;
+			reply.xclient.data.l[1] = 0;
+			reply.xclient.data.l[2] = None;
+
+			if (E.xclient.message_type == XdndEnter) {
+				unsigned long count;
+				Atom* formats;
+				Atom real_formats[6];
+
+				Bool list = E.xclient.data.l[1] & 1;
+
+				xdnd.source = E.xclient.data.l[0];
+				xdnd.version = E.xclient.data.l[1] >> 24;
+				xdnd.format = None;
+
+				if (xdnd.version > 5)
+					break;
+
+				if (list) {
+					Atom actualType;
+					i32 actualFormat;
+					unsigned long bytesAfter;
+
+					XGetWindowProperty((Display*) win->src.display,
+						xdnd.source,
+						XdndTypeList,
+						0,
+						LONG_MAX,
+						False,
+						4,
+						&actualType,
+						&actualFormat,
+						&count,
+						&bytesAfter,
+						(u8**) &formats);
+				} else {
+					count = 0;
+
+					if (E.xclient.data.l[2] != None)
+						real_formats[count++] = E.xclient.data.l[2];
+					if (E.xclient.data.l[3] != None)
+						real_formats[count++] = E.xclient.data.l[3];
+					if (E.xclient.data.l[4] != None)
+						real_formats[count++] = E.xclient.data.l[4];
+					
+					formats = real_formats;
+				}
+
+				unsigned long i;
+				for (i = 0; i < count; i++) {
+				    if (formats[i] == XtextUriList || formats[i] == XtextPlain) {
+						xdnd.format = formats[i];
+						break;
+					}
+				}
+
+				if (list) {
+					XFree(formats);
+				}
+
+				break;
+			}
+			if (E.xclient.message_type == XdndPosition) {
+				const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff;
+				const i32 yabs = (E.xclient.data.l[2]) & 0xffff;
+				Window dummy;
+				i32 xpos, ypos;
+
+				if (xdnd.version > 5)
+					break;
+
+				XTranslateCoordinates((Display*) win->src.display,
+					XDefaultRootWindow((Display*) win->src.display),
+					(Window) win->src.window,
+					xabs, yabs,
+					&xpos, &ypos,
+					&dummy);
+
+				win->event.point.x = xpos;
+				win->event.point.y = ypos;
+
+				reply.xclient.window = xdnd.source;
+				reply.xclient.message_type = XdndStatus;
+
+				if (xdnd.format) {
+					reply.xclient.data.l[1] = 1;
+					if (xdnd.version >= 2)
+						reply.xclient.data.l[4] = XdndActionCopy;
+				}
+
+				XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply);
+				XFlush((Display*) win->src.display);
+				break;
+			}
+
+			if (E.xclient.message_type != XdndDrop)
+				break;
+
+			if (xdnd.version > 5)
+				break;
+
+			win->event.type = RGFW_dnd_init;
+
+			if (xdnd.format) {
+				Time time = CurrentTime;
+
+				if (xdnd.version >= 1)
+					time = E.xclient.data.l[2];
+
+				XConvertSelection((Display*) win->src.display,
+					XdndSelection,
+					xdnd.format,
+					XdndSelection,
+					(Window) win->src.window,
+					time);
+			} else if (xdnd.version >= 2) {
+				XEvent reply = { ClientMessage };
+
+				XSendEvent((Display*) win->src.display, xdnd.source,
+					False, NoEventMask, &reply);
+				XFlush((Display*) win->src.display);
+			}
+
+			RGFW_dndInitCallback(win, win->event.point);
+			break;
+		case SelectionNotify: {
+			/* this is only for checking for xdnd drops */
+			if (E.xselection.property != XdndSelection || !(win->_winArgs | RGFW_ALLOW_DND))
+				break;
+
+			char* data;
+			unsigned long result;
+
+			Atom actualType;
+			i32 actualFormat;
+			unsigned long bytesAfter;
+
+			XGetWindowProperty((Display*) win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data);
+
+			if (result == 0)
+				break;
+
+			/*
+			SOURCED FROM GLFW _glfwParseUriList
+			Copyright (c) 2002-2006 Marcus Geelnard
+			Copyright (c) 2006-2019 Camilla Löwy
+			*/
+
+			const char* prefix = (const char*)"file://";
+
+			char* line;
+
+			win->event.droppedFilesCount = 0;
+
+			win->event.type = RGFW_dnd;
+
+			while ((line = strtok(data, "\r\n"))) {
+				char path[RGFW_MAX_PATH];
+
+				data = NULL;
+
+				if (line[0] == '#')
+					continue;
+
+				char* l;
+				for (l = line; 1; l++) {
+					if ((l - line) > 7)
+						break;
+					else if (*l != prefix[(l - line)])
+						break;
+					else if (*l == '\0' && prefix[(l - line)] == '\0') {
+						line += 7;
+						while (*line != '/')
+							line++;
+						break;
+					} else if (*l == '\0')
+						break;
+				}
+
+				win->event.droppedFilesCount++;
+
+				size_t index = 0;
+				while (*line) {
+					if (line[0] == '%' && line[1] && line[2]) {
+						const char digits[3] = { line[1], line[2], '\0' };
+						path[index] = (char) strtol(digits, NULL, 16);
+						line += 2;
+					} else
+						path[index] = *line;
+
+					index++;
+					line++;
+				}
+				path[index] = '\0';
+				strncpy(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1);
+			}
+
+			if (data)
+				XFree(data);
+
+			if (xdnd.version >= 2) {
+				reply.xclient.message_type = XdndFinished;
+				reply.xclient.data.l[1] = result;
+				reply.xclient.data.l[2] = XdndActionCopy;
+
+				XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply);
+				XFlush((Display*) win->src.display);
+			}
+
+			RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
+			break;
+		}
+		case FocusIn:
+			win->event.inFocus = 1;
+			win->event.type = RGFW_focusIn;
+			RGFW_focusCallback(win, 1);
+			break;
+
+			break;
+		case FocusOut:
+			win->event.inFocus = 0;
+			win->event.type = RGFW_focusOut;
+			RGFW_focusCallback(win, 0);
+			break;
+		
+		case EnterNotify: {
+			win->event.type = RGFW_mouseEnter;
+			win->event.point.x = E.xcrossing.x;
+			win->event.point.y = E.xcrossing.y;
+			RGFW_mouseNotifyCallBack(win, win->event.point, 1);
+			break;
+		}
+
+		case LeaveNotify: {
+			win->event.type = RGFW_mouseLeave;
+			RGFW_mouseNotifyCallBack(win, win->event.point, 0);
+			break;
+		}
+
+		case ConfigureNotify: {
+				/* detect resize */
+      			if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) {
+					win->event.type = RGFW_windowResized;
+					win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height);
+					RGFW_windowResizeCallback(win, win->r);
+					break;
+      			}  
+      
+      			/* detect move */
+      			if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) {
+					win->event.type = RGFW_windowMoved;
+					win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h);
+					RGFW_windowMoveCallback(win, win->r);
+					break;
+				} 
+
+				break;
+		}
+		default: {
+			break;
+		}
+		}
+
+		XFlush((Display*) win->src.display);
+
+		if (win->event.type)
+			return &win->event;
+		else
+			return NULL;
+	}
+
+	void RGFW_window_move(RGFW_window* win, RGFW_point v) {
+		assert(win != NULL);
+		win->r.x = v.x;
+		win->r.y = v.y;
+
+		XMoveWindow((Display*) win->src.display, (Window) win->src.window, v.x, v.y);
+	}
+
+
+	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
+		assert(win != NULL);
+		win->r.w = a.w;
+		win->r.h = a.h;
+
+		XResizeWindow((Display*) win->src.display, (Window) win->src.window, a.w, a.h);
+	}
+
+	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
+		assert(win != NULL);
+
+		if (a.w == 0 && a.h == 0)
+			return;
+
+		XSizeHints hints;
+		long flags;
+
+		XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags);
+
+		hints.flags |= PMinSize;
+		
+		hints.min_width = a.w;
+		hints.min_height = a.h;
+
+		XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints);
+	}
+
+	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
+		assert(win != NULL);
+
+		if (a.w == 0 && a.h == 0)
+			return;
+
+		XSizeHints hints;
+		long flags;
+
+		XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags);
+
+		hints.flags |= PMaxSize;
+
+		hints.max_width = a.w;
+		hints.max_height = a.h;
+
+		XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints);
+	}
+
+
+	void RGFW_window_minimize(RGFW_window* win) {
+		assert(win != NULL);
+
+		XIconifyWindow(win->src.display, (Window) win->src.window, DefaultScreen(win->src.display));
+		XFlush(win->src.display);
+	}
+
+	void RGFW_window_restore(RGFW_window* win) {
+		assert(win != NULL);
+
+		XMapWindow(win->src.display, (Window) win->src.window);
+		XFlush(win->src.display);
+	}	
+
+	void RGFW_window_setName(RGFW_window* win, char* name) {
+		assert(win != NULL);
+
+		XStoreName((Display*) win->src.display, (Window) win->src.window, name);
+	}
+	
+	void* RGFW_libxshape = NULL;
+
+	#ifndef RGFW_NO_PASSTHROUGH
+	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
+		assert(win != NULL);
+		
+		#if defined(__CYGWIN__)
+			RGFW_libxshape = dlopen("libXext-6.so", RTLD_LAZY | RTLD_LOCAL);
+		#elif defined(__OpenBSD__) || defined(__NetBSD__)
+			RGFW_libxshape = dlopen("libXext.so", RTLD_LAZY | RTLD_LOCAL);
+		#else
+    		RGFW_libxshape = dlopen("libXext.so.6", RTLD_LAZY | RTLD_LOCAL);
+		#endif
+		
+		typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
+		static PFN_XShapeCombineMask XShapeCombineMask;
+		
+		typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);
+		static PFN_XShapeCombineRegion XShapeCombineRegion;
+		
+		if (XShapeCombineMask != NULL)
+			XShapeCombineMask = (PFN_XShapeCombineMask) dlsym(RGFW_libxshape, "XShapeCombineMask");
+
+		if (XShapeCombineRegion != NULL)
+			XShapeCombineRegion = (PFN_XShapeCombineRegion) dlsym(RGFW_libxshape, "XShapeCombineMask");
+
+		if (passthrough) {
+			Region region = XCreateRegion();
+			XShapeCombineRegion(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet);
+			XDestroyRegion(region);
+
+			return;
+		}
+
+		XShapeCombineMask(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet);
+	}
+	#endif
+
+	/*
+		the majority function is sourced from GLFW
+	*/
+
+	void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) {
+		assert(win != NULL);
+
+		i32 longCount = 2 + a.w * a.h;
+
+		u64* X11Icon = (u64*) RGFW_MALLOC(longCount * sizeof(u64));
+		u64* target = X11Icon;
+
+		*target++ = a.w;
+		*target++ = a.h;
+
+		u32 i;
+
+		for (i = 0; i < a.w * a.h; i++) {
+			if (channels == 3)
+				*target++ = ((icon[i * 3 + 0]) << 16) |
+				((icon[i * 3 + 1]) << 8) |
+				((icon[i * 3 + 2]) << 0) |
+				(0xFF << 24);
+
+			else if (channels == 4)
+				*target++ = ((icon[i * 4 + 0]) << 16) |
+				((icon[i * 4 + 1]) << 8) |
+				((icon[i * 4 + 2]) << 0) |
+				((icon[i * 4 + 3]) << 24);
+		}
+
+		static Atom NET_WM_ICON = 0;
+		if (NET_WM_ICON == 0)
+			NET_WM_ICON = XInternAtom((Display*) win->src.display, "_NET_WM_ICON", False);
+
+		XChangeProperty((Display*) win->src.display, (Window) win->src.window,
+			NET_WM_ICON,
+			6, 32,
+			PropModeReplace,
+			(u8*) X11Icon,
+			longCount);
+
+		RGFW_FREE(X11Icon);
+
+		XFlush((Display*) win->src.display);
+	}
+
+	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
+		assert(win != NULL);
+
+#ifndef RGFW_NO_X11_CURSOR
+		XcursorImage* native = XcursorImageCreate(a.w, a.h);
+		native->xhot = 0;
+		native->yhot = 0;
+
+		u8* source = (u8*) image;
+		XcursorPixel* target = native->pixels;
+
+		u32 i;
+		for (i = 0; i < a.w * a.h; i++, target++, source += 4) {
+			u8 alpha = 0xFF;
+			if (channels == 4)
+				alpha = source[3];
+
+			*target = (alpha << 24) | (((source[0] * alpha) / 255) << 16) | (((source[1] * alpha) / 255) << 8) | (((source[2] * alpha) / 255) << 0);
+		}
+
+		Cursor cursor = XcursorImageLoadCursor((Display*) win->src.display, native);
+		XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor);
+
+		XFreeCursor((Display*) win->src.display, (Cursor) cursor);
+		XcursorImageDestroy(native);
+#else
+	RGFW_UNUSED(image) RGFW_UNUSED(a.w) RGFW_UNUSED(channels)
+#endif
+	}
+
+	void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) {
+		assert(win != NULL);
+
+		XEvent event;
+		XQueryPointer(win->src.display, DefaultRootWindow(win->src.display),
+			&event.xbutton.root, &event.xbutton.window,
+			&event.xbutton.x_root, &event.xbutton.y_root,
+			&event.xbutton.x, &event.xbutton.y,
+			&event.xbutton.state);
+
+		if (event.xbutton.x == v.x && event.xbutton.y == v.y)
+			return;
+
+		XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) v.x - win->r.x, (int) v.y - win->r.y);
+	}
+
+	RGFWDEF void RGFW_window_disableMouse(RGFW_window* win) {
+		RGFW_UNUSED(win);
+	}
+
+	void RGFW_window_setMouseDefault(RGFW_window* win) {
+		RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW);
+	}
+
+	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
+		assert(win != NULL);
+		 
+		if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u8)))
+			return;
+		
+		mouse = RGFW_mouseIconSrc[mouse];
+
+		Cursor cursor = XCreateFontCursor((Display*) win->src.display, mouse);
+		XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor);
+
+		XFreeCursor((Display*) win->src.display, (Cursor) cursor);
+	}
+
+	void RGFW_window_hide(RGFW_window* win) {
+		XMapWindow(win->src.display, win->src.window);
+	}
+
+	void RGFW_window_show(RGFW_window* win) {
+		XUnmapWindow(win->src.display, win->src.window);
+	}
+
+	/*
+		the majority function is sourced from GLFW
+	*/
+	char* RGFW_readClipboard(size_t* size) {
+		static Atom UTF8 = 0;
+		if (UTF8 == 0)
+			UTF8 = XInternAtom(RGFW_root->src.display, "UTF8_STRING", True);
+
+		XEvent event;
+		int format;
+		unsigned long N, sizeN;
+		char* data, * s = NULL;
+		Atom target;
+		Atom CLIPBOARD = 0, XSEL_DATA = 0;
+
+		if (CLIPBOARD == 0) {
+			CLIPBOARD = XInternAtom(RGFW_root->src.display, "CLIPBOARD", 0);
+			XSEL_DATA = XInternAtom(RGFW_root->src.display, "XSEL_DATA", 0);
+		}
+
+		XConvertSelection(RGFW_root->src.display, CLIPBOARD, UTF8, XSEL_DATA, RGFW_root->src.window, CurrentTime);
+		XSync(RGFW_root->src.display, 0);
+		XNextEvent(RGFW_root->src.display, &event);
+
+		if (event.type != SelectionNotify || event.xselection.selection != CLIPBOARD || event.xselection.property == 0)
+			return NULL;
+
+		XGetWindowProperty(event.xselection.display, event.xselection.requestor,
+			event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target,
+			&format, &sizeN, &N, (unsigned char**) &data);
+
+		if (target == UTF8 || target == XA_STRING) {
+			s = (char*)RGFW_MALLOC(sizeof(char) * sizeN);
+			strncpy(s, data, sizeN);
+			s[sizeN] = '\0';
+			XFree(data);
+		}
+
+		XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
+
+		if (s != NULL && size != NULL)
+			*size = sizeN;
+
+		return s;
+	}
+
+	/*
+		almost all of this function is sourced from GLFW
+	*/
+	void RGFW_writeClipboard(const char* text, u32 textLen) {
+		static Atom CLIPBOARD = 0,
+			UTF8_STRING = 0,
+			SAVE_TARGETS = 0,
+			TARGETS = 0,
+			MULTIPLE = 0,
+			ATOM_PAIR = 0,
+			CLIPBOARD_MANAGER = 0;
+
+		if (CLIPBOARD == 0) {
+			CLIPBOARD = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD", False);
+			UTF8_STRING = XInternAtom((Display*) RGFW_root->src.display, "UTF8_STRING", False);
+			SAVE_TARGETS = XInternAtom((Display*) RGFW_root->src.display, "SAVE_TARGETS", False);
+			TARGETS = XInternAtom((Display*) RGFW_root->src.display, "TARGETS", False);
+			MULTIPLE = XInternAtom((Display*) RGFW_root->src.display, "MULTIPLE", False);
+			ATOM_PAIR = XInternAtom((Display*) RGFW_root->src.display, "ATOM_PAIR", False);
+			CLIPBOARD_MANAGER = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD_MANAGER", False);
+		}
+		
+		XSetSelectionOwner((Display*) RGFW_root->src.display, CLIPBOARD, (Window) RGFW_root->src.window, CurrentTime);
+
+		XConvertSelection((Display*) RGFW_root->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, (Window) RGFW_root->src.window, CurrentTime);
+		for (;;) {
+			XEvent event;
+
+			XNextEvent((Display*) RGFW_root->src.display, &event);
+			if (event.type != SelectionRequest) {
+				break;
+			}
+
+			const XSelectionRequestEvent* request = &event.xselectionrequest;
+
+			XEvent reply = { SelectionNotify };
+			reply.xselection.property = 0;
+
+			if (request->target == TARGETS) {
+				const Atom targets[] = { TARGETS,
+										MULTIPLE,
+										UTF8_STRING,
+										XA_STRING };
+
+				XChangeProperty((Display*) RGFW_root->src.display,
+					request->requestor,
+					request->property,
+					4,
+					32,
+					PropModeReplace,
+					(u8*) targets,
+					sizeof(targets) / sizeof(targets[0]));
+
+				reply.xselection.property = request->property;
+			}
+
+			if (request->target == MULTIPLE) {
+				Atom* targets = NULL;
+
+				Atom actualType = 0;
+				int actualFormat = 0;
+				unsigned long count = 0, bytesAfter = 0;
+
+				XGetWindowProperty((Display*) RGFW_root->src.display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets);
+
+				unsigned long i;
+				for (i = 0; i < (u32)count; i += 2) {
+					if (targets[i] == UTF8_STRING || targets[i] == XA_STRING) {
+						XChangeProperty((Display*) RGFW_root->src.display,
+							request->requestor,
+							targets[i + 1],
+							targets[i],
+							8,
+							PropModeReplace,
+							(u8*) text,
+							textLen);
+						XFlush(RGFW_root->src.display);
+					} else {
+						targets[i + 1] = None;
+					}
+				}
+
+				XChangeProperty((Display*) RGFW_root->src.display,
+					request->requestor,
+					request->property,
+					ATOM_PAIR,
+					32,
+					PropModeReplace,
+					(u8*) targets,
+					count);
+
+				XFlush(RGFW_root->src.display);
+				XFree(targets);
+
+				reply.xselection.property = request->property;
+			}
+
+			reply.xselection.display = request->display;
+			reply.xselection.requestor = request->requestor;
+			reply.xselection.selection = request->selection;
+			reply.xselection.target = request->target;
+			reply.xselection.time = request->time;
+
+			XSendEvent((Display*) RGFW_root->src.display, request->requestor, False, 0, &reply);
+			XFlush(RGFW_root->src.display);
+		}
+	}
+
+	u8 RGFW_window_isFullscreen(RGFW_window* win) {
+		assert(win != NULL);
+
+		XWindowAttributes windowAttributes;
+		XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes);
+
+		/* check if the window is visable */
+		if (windowAttributes.map_state != IsViewable)
+			return 0;
+
+		/* check if the window covers the full screen */
+		return (windowAttributes.x == 0 && windowAttributes.y == 0 &&
+			windowAttributes.width == XDisplayWidth(win->src.display, DefaultScreen(win->src.display)) &&
+			windowAttributes.height == XDisplayHeight(win->src.display, DefaultScreen(win->src.display)));
+	}
+
+	u8 RGFW_window_isHidden(RGFW_window* win) {
+		assert(win != NULL);
+
+		XWindowAttributes windowAttributes;
+		XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes);
+
+		return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win));
+	}
+
+	u8 RGFW_window_isMinimized(RGFW_window* win) {
+		assert(win != NULL);
+
+		static Atom prop = 0;
+		if (prop == 0)
+			prop = XInternAtom(win->src.display, "WM_STATE", False);
+
+		Atom actual_type;
+		i32 actual_format;
+		unsigned long nitems, bytes_after;
+		unsigned char* prop_data;
+
+		i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, prop, 0, 2, False,
+			AnyPropertyType, &actual_type, &actual_format,
+			&nitems, &bytes_after, &prop_data);
+
+		if (status == Success && nitems >= 1 && *((int*) prop_data) == IconicState) {
+			XFree(prop_data);
+			return 1;
+		}
+
+		if (prop_data != NULL)
+			XFree(prop_data);
+
+		return 0;
+	}
+
+	u8 RGFW_window_isMaximized(RGFW_window* win) {
+		assert(win != NULL);
+
+		static Atom net_wm_state = 0;
+		static Atom net_wm_state_maximized_horz = 0;
+		static Atom net_wm_state_maximized_vert = 0;
+
+		if (net_wm_state == 0) {
+			net_wm_state = XInternAtom(win->src.display, "_NET_WM_STATE", False);
+			net_wm_state_maximized_vert = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
+			net_wm_state_maximized_horz = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
+		}
+
+		Atom actual_type;
+		i32 actual_format;
+		unsigned long nitems, bytes_after;
+		unsigned char* prop_data;
+
+		i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, net_wm_state, 0, 1024, False,
+			XA_ATOM, &actual_type, &actual_format,
+			&nitems, &bytes_after, &prop_data);
+
+		if (status != Success) {
+			if (prop_data != NULL)
+				XFree(prop_data);
+
+			return 0;
+		}
+
+		Atom* atoms = (Atom*) prop_data;
+		u64 i;
+		for (i = 0; i < nitems; ++i) {
+			if (atoms[i] == net_wm_state_maximized_horz ||
+				atoms[i] == net_wm_state_maximized_vert) {
+				XFree(prop_data);
+				return 1;
+			}
+		}
+
+		return 0;
+	}
+
+	static void XGetSystemContentScale(Display* display, float* xscale, float* yscale) {
+		float xdpi = 96.f, ydpi = 96.f;
+
+#ifndef RGFW_NO_DPI
+		char* rms = XResourceManagerString(display);
+		XrmDatabase db = NULL;
+
+		if (rms && db)
+			db = XrmGetStringDatabase(rms);
+
+		if (db == 0) {
+			*xscale = xdpi / 96.f;
+			*yscale = ydpi / 96.f;
+			return;
+		}
+
+		XrmValue value;
+		char* type = NULL;
+
+		if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && strncmp(type, "String", 7) == 0)
+			xdpi = ydpi = atof(value.addr);
+		XrmDestroyDatabase(db);
+#endif
+
+		* xscale = xdpi / 96.f;
+		*yscale = ydpi / 96.f;
+	}
+
+	RGFW_monitor RGFW_XCreateMonitor(i32 screen) {
+		RGFW_monitor monitor;
+
+		Display* display = XOpenDisplay(NULL);
+		
+		RGFW_area size = RGFW_getScreenSize();
+
+		monitor.rect = RGFW_RECT(0, 0, size.w, size.h);
+		monitor.physW = DisplayWidthMM(display, screen);
+		monitor.physH = DisplayHeightMM(display, screen);
+		
+		XGetSystemContentScale(display, &monitor.scaleX, &monitor.scaleY);
+		XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen));
+
+		XRRCrtcInfo* ci = NULL;
+		int crtc = screen;
+
+		if (sr->ncrtc > crtc) {
+			ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]);
+		}
+		
+		if (ci == NULL) {
+			float dpi_width = round((double)monitor.rect.w/(((double)monitor.physW)/25.4));
+			float dpi_height = round((double)monitor.rect.h/(((double)monitor.physH)/25.4));
+		
+			monitor.scaleX = (float) (dpi_width) / (float) 96;
+			monitor.scaleY = (float) (dpi_height) / (float) 96;
+			XRRFreeScreenResources(sr);
+			XCloseDisplay(display);
+			return monitor;
+		}
+	    
+		XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]);
+		monitor.physW = info->mm_width;
+		monitor.physH = info->mm_height;
+
+		monitor.rect.x = ci->x;
+		monitor.rect.y = ci->y;
+		monitor.rect.w = ci->width;
+		monitor.rect.h = ci->height;
+		
+		float dpi_width = round((double)monitor.rect.w/(((double)monitor.physW)/25.4));
+		float dpi_height = round((double)monitor.rect.h/(((double)monitor.physH)/25.4));
+
+		monitor.scaleX = (float) (dpi_width) / (float) 96;
+		monitor.scaleY = (float) (dpi_height) / (float) 96;		
+
+		if (monitor.scaleX > 1 && monitor.scaleX < 1.1)
+			monitor.scaleX = 1;
+
+		if (monitor.scaleY > 1 && monitor.scaleY < 1.1)
+			monitor.scaleY = 1;
+
+		XRRFreeCrtcInfo(ci);
+		XRRFreeScreenResources(sr);
+
+		XCloseDisplay(display);
+
+		return monitor;
+	}
+
+	RGFW_monitor RGFW_monitors[6];
+	RGFW_monitor* RGFW_getMonitors(void) {
+		size_t i;
+		for (i = 0; i < (size_t)ScreenCount(RGFW_root->src.display) && i < 6; i++)
+			RGFW_monitors[i] = RGFW_XCreateMonitor(i);
+
+		return RGFW_monitors;
+	}
+
+	RGFW_monitor RGFW_getPrimaryMonitor(void) {
+		assert(RGFW_root != NULL);
+
+		i32 primary = -1;
+		Window root = DefaultRootWindow(RGFW_root->src.display);
+		XRRScreenResources* res = XRRGetScreenResources(RGFW_root->src.display, root);
+
+		for (int i = 0; i < res->noutput; i++) {
+			XRROutputInfo* output_info = XRRGetOutputInfo(RGFW_root->src.display, res, res->outputs[i]);
+			if (output_info->connection == RR_Connected && output_info->crtc) {
+				XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(RGFW_root->src.display, res, output_info->crtc);
+				if (crtc_info->mode != None && crtc_info->x == 0 && crtc_info->y == 0) {
+					primary = i;
+					XRRFreeCrtcInfo(crtc_info);
+					XRRFreeOutputInfo(output_info);
+					break;
+				}
+				XRRFreeCrtcInfo(crtc_info);
+			}
+			XRRFreeOutputInfo(output_info);
+		}
+
+		XRRFreeScreenResources(res);
+
+		return RGFW_XCreateMonitor(primary);
+	}
+
+	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
+		return RGFW_XCreateMonitor(DefaultScreen(win->src.display));
+	}
+
+	#ifdef RGFW_OPENGL
+	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+		if (win == NULL)
+			glXMakeCurrent((Display*) NULL, (Drawable)NULL, (GLXContext) NULL);
+		else
+			glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx);
+	}
+	#endif
+
+
+	void RGFW_window_swapBuffers(RGFW_window* win) {
+		assert(win != NULL);
+
+		/* clear the window*/
+		if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) {
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+			#ifdef RGFW_OSMESA
+			RGFW_OSMesa_reorganize();
+			#endif
+			RGFW_area area = RGFW_bufferSize;
+
+#ifndef RGFW_X11_DONT_CONVERT_BGR
+			win->src.bitmap->data = (char*) win->buffer;
+			u32 x, y;
+			for (y = 0; y < (u32)win->r.h; y++) {
+				for (x = 0; x < (u32)win->r.w; x++) {
+					u32 index = (y * 4 * area.w) + x * 4;
+
+					u8 red = win->src.bitmap->data[index];
+					win->src.bitmap->data[index] = win->buffer[index + 2];
+					win->src.bitmap->data[index + 2] = red;
+
+				}
+			}
+#endif	
+			XPutImage(win->src.display, (Window) win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, RGFW_bufferSize.w, RGFW_bufferSize.h);
+#endif
+		}
+
+		if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) {
+			#ifdef RGFW_EGL
+					eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
+			#elif defined(RGFW_OPENGL)
+					glXSwapBuffers((Display*) win->src.display, (Window) win->src.window);
+			#endif
+		}
+	}
+
+	#if !defined(RGFW_EGL)	
+	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
+		assert(win != NULL);
+
+		#if defined(RGFW_OPENGL)	
+		((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))((Display*) win->src.display, (Window) win->src.window, swapInterval);
+		#else
+		RGFW_UNUSED(swapInterval);
+		#endif
+	}
+	#endif
+
+
+	void RGFW_window_close(RGFW_window* win) {
+		/* ungrab pointer if it was grabbed */
+		if (win->_winArgs & RGFW_HOLD_MOUSE) 
+			XUngrabPointer(win->src.display, CurrentTime);
+			
+		assert(win != NULL);
+#ifdef RGFW_EGL
+		RGFW_closeEGL(win);
+#endif
+
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+		if (win->buffer != NULL) {
+			XDestroyImage((XImage*) win->src.bitmap);
+			XFreeGC(win->src.display, win->src.gc);
+		}
+#endif
+
+		if ((Display*) win->src.display) {
+#ifdef RGFW_OPENGL
+			glXDestroyContext((Display*) win->src.display, win->src.ctx);
+#endif
+
+			if (win == RGFW_root)
+				RGFW_root = NULL;
+
+			if ((Drawable) win->src.window)
+				XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /*!< close the window*/
+			
+			XCloseDisplay((Display*) win->src.display); /*!< kill the display*/
+		}
+
+#ifdef RGFW_ALLOC_DROPFILES
+		{
+			u32 i;
+			for (i = 0; i < RGFW_MAX_DROPS; i++)
+				RGFW_FREE(win->event.droppedFiles[i]);
+
+
+			RGFW_FREE(win->event.droppedFiles);
+		}
+#endif
+
+		RGFW_windowsOpen--;
+#if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR)
+		if (X11Cursorhandle != NULL && RGFW_windowsOpen <= 0) {
+			dlclose(X11Cursorhandle);
+
+			X11Cursorhandle = NULL;
+		}
+#endif
+#if !defined(RGFW_NO_X11_XI_PRELOAD)
+		if (X11Xihandle != NULL && RGFW_windowsOpen <= 0) {
+			dlclose(X11Xihandle);
+
+			X11Xihandle = NULL;
+		}
+#endif
+
+		if (RGFW_libxshape != NULL && RGFW_windowsOpen <= 0) {
+			dlclose(RGFW_libxshape);
+			RGFW_libxshape = NULL;
+		}
+
+		if (RGFW_windowsOpen <= 0) {
+			if (RGFW_eventWait_forceStop[0] || RGFW_eventWait_forceStop[1]){
+				close(RGFW_eventWait_forceStop[0]);
+				close(RGFW_eventWait_forceStop[1]);
+			}
+
+			u8 i;
+			for (i = 0; i < RGFW_joystickCount; i++)
+				close(RGFW_joysticks[i]);
+		}
+
+		/* set cleared display / window to NULL for error checking */
+		win->src.display = (Display*) 0;
+		win->src.window = (Window) 0;
+
+		RGFW_FREE(win); /*!< free collected window data */
+	}
+	
+
+/* 
+	End of X11 linux / unix defines
+*/
+
+#endif /* RGFW_X11 */
+
+
+/* wayland or X11 defines*/
+#if defined(RGFW_WAYLAND) || defined(RGFW_X11)
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+	u16 RGFW_registerJoystickF(RGFW_window* win, char* file) {
+		assert(win != NULL);
+
+#ifdef __linux__
+
+		i32 js = open(file, O_RDONLY);
+
+		if (js && RGFW_joystickCount < 4) {
+			RGFW_joystickCount++;
+
+			RGFW_joysticks[RGFW_joystickCount - 1] = open(file, O_RDONLY);
+
+			u8 i;
+			for (i = 0; i < 16; i++)
+				RGFW_jsPressed[RGFW_joystickCount - 1][i] = 0;
+
+		}
+
+		else {
+#ifdef RGFW_PRINT_ERRORS
+			RGFW_error = 1;
+			fprintf(stderr, "Error RGFW_registerJoystickF : Cannot open file %s\n", file);
+#endif
+		}
+
+		return RGFW_joystickCount - 1;
+#endif
+	}
+	
+	u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) {
+		assert(win != NULL);
+
+#ifdef __linux__
+		char file[15];
+		sprintf(file, "/dev/input/js%i", jsNumber);
+
+		return RGFW_registerJoystickF(win, file);
+#endif
+	}
+	
+	void RGFW_stopCheckEvents(void) { 
+		RGFW_eventWait_forceStop[2] = 1;
+		while (1) {
+			const char byte = 0;
+			const ssize_t result = write(RGFW_eventWait_forceStop[1], &byte, 1);
+			if (result == 1 || result == -1)
+				break;
+		}
+	}
+
+	void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
+		if (waitMS == 0)
+			return;
+		
+		u8 i;
+
+		if (RGFW_eventWait_forceStop[0] == 0 || RGFW_eventWait_forceStop[1] == 0) {
+			if (pipe(RGFW_eventWait_forceStop) != -1) {
+				fcntl(RGFW_eventWait_forceStop[0], F_GETFL, 0);
+				fcntl(RGFW_eventWait_forceStop[0], F_GETFD, 0);
+				fcntl(RGFW_eventWait_forceStop[1], F_GETFL, 0);
+				fcntl(RGFW_eventWait_forceStop[1], F_GETFD, 0);
+			}
+		}
+
+		struct pollfd fds[] = {
+			#ifdef RGFW_WAYLAND
+			{ wl_display_get_fd(win->src.display), POLLIN, 0 },
+			#else
+			{ ConnectionNumber(win->src.display), POLLIN, 0 },
+			#endif
+			{ RGFW_eventWait_forceStop[0], POLLIN, 0 },
+			#ifdef __linux__ /* blank space for 4 joystick files*/
+			{ -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 },  {-1, POLLIN, 0} 
+			#endif
+		};
+
+		u8 index = 2;
+		
+		#if defined(__linux__)
+			for (i = 0; i < RGFW_joystickCount; i++) {
+				if (RGFW_joysticks[i] == 0)
+					continue;
+
+				fds[index].fd = RGFW_joysticks[i];
+				index++;
+			}
+		#endif
+
+
+		u64 start = RGFW_getTimeNS();
+
+		#ifdef RGFW_WAYLAND
+		while (wl_display_dispatch(win->src.display) <= 0 && waitMS >= -1) {
+		#else
+		while (XPending(win->src.display) == 0 && waitMS >= -1) {
+		#endif
+			if (poll(fds, index, waitMS) <= 0)
+				break;
+
+			if (waitMS > 0) {
+				waitMS -= (RGFW_getTimeNS() - start) / 1e+6;
+			}
+		}
+
+		/* drain any data in the stop request */
+		if (RGFW_eventWait_forceStop[2]) {	
+			char data[64];
+			(void)!read(RGFW_eventWait_forceStop[0], data, sizeof(data));
+			
+			RGFW_eventWait_forceStop[2] = 0;
+		}
+	}
+
+	u64 RGFW_getTimeNS(void) { 
+		struct timespec ts = { 0 };
+		clock_gettime(1, &ts);
+		unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
+
+		return nanoSeconds;
+	}
+
+	u64 RGFW_getTime(void) {
+		struct timespec ts = { 0 };
+		clock_gettime(1, &ts);
+		unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
+
+		return (double)(nanoSeconds) * 1e-9;
+	}
+#endif /* end of wayland or X11 time defines*/
+
+
+/*
+
+	Start of Wayland defines
+
+
+*/
+
+#ifdef RGFW_WAYLAND
+/*
+Wayland TODO:
+- fix RGFW_keyPressed lock state
+
+	RGFW_windowMoved, 		the window was moved (by the user)
+	RGFW_windowResized  	the window was resized (by the user), [on webASM this means the browser was resized]
+	RGFW_windowRefresh	 	The window content needs to be refreshed
+
+	RGFW_dnd 				a file has been dropped into the window
+	RGFW_dnd_init
+
+- window args:
+	#define RGFW_NO_RESIZE	 			the window cannot be resized  by the user
+	#define RGFW_ALLOW_DND     			the window supports drag and drop
+	#define RGFW_SCALE_TO_MONITOR 			scale the window to the screen 
+
+- other missing functions functions ("TODO wayland") (~30 functions)
+- fix buffer rendering weird behavior
+*/
+	#include <errno.h>
+	#include <unistd.h>
+	#include <sys/mman.h>
+	#include <xkbcommon/xkbcommon.h>
+	#include <xkbcommon/xkbcommon-keysyms.h>
+	#include <dirent.h>
+	#include <linux/kd.h> 
+	#include <wayland-cursor.h>
+
+RGFW_window* RGFW_key_win = NULL;
+
+void RGFW_eventPipe_push(RGFW_window* win, RGFW_Event event) {
+	if (win == NULL) {
+		win = RGFW_key_win;
+
+		if (win == NULL) return;
+	}
+	
+	if (win->src.eventLen >= (i32)(sizeof(win->src.events) / sizeof(win->src.events[0])))
+		return;
+
+	win->src.events[win->src.eventLen] = event;
+	win->src.eventLen += 1;
+}
+
+RGFW_Event RGFW_eventPipe_pop(RGFW_window* win) {
+	RGFW_Event ev;
+	ev.type = 0;
+	
+	if (win->src.eventLen > -1)
+		win->src.eventLen -= 1;
+	
+	if (win->src.eventLen >= 0)  
+		ev = win->src.events[win->src.eventLen];
+
+	return ev;	
+}
+
+/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */
+#include "xdg-shell.h"
+#include "xdg-decoration-unstable-v1.h"
+
+struct xdg_wm_base *xdg_wm_base;
+struct wl_compositor* RGFW_compositor = NULL;
+struct wl_shm* shm = NULL;
+struct wl_shell* RGFW_shell = NULL;
+static struct wl_seat *seat = NULL;
+static struct xkb_context *xkb_context;
+static struct xkb_keymap *keymap = NULL;
+static struct xkb_state *xkb_state = NULL;
+enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode;
+static struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
+
+struct wl_cursor_theme* RGFW_wl_cursor_theme = NULL;
+struct wl_surface* RGFW_cursor_surface = NULL;
+struct wl_cursor_image* RGFW_cursor_image = NULL;
+
+static void xdg_wm_base_ping_handler(void *data,
+        struct xdg_wm_base *wm_base, uint32_t serial)
+{
+	RGFW_UNUSED(data);
+    xdg_wm_base_pong(wm_base, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+    .ping = xdg_wm_base_ping_handler,
+};
+
+b8 RGFW_wl_configured = 0;
+
+static void xdg_surface_configure_handler(void *data,
+        struct xdg_surface *xdg_surface, uint32_t serial)
+{	
+	RGFW_UNUSED(data);
+    xdg_surface_ack_configure(xdg_surface, serial);
+	#ifdef RGFW_DEBUG
+	printf("Surface configured\n");
+	#endif
+	RGFW_wl_configured = 1;
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+    .configure = xdg_surface_configure_handler,
+};
+
+static void xdg_toplevel_configure_handler(void *data,
+        struct xdg_toplevel *toplevel, int32_t width, int32_t height,
+        struct wl_array *states)
+{
+	RGFW_UNUSED(data); RGFW_UNUSED(toplevel); RGFW_UNUSED(states)
+    fprintf(stderr, "XDG toplevel configure: %dx%d\n", width, height);
+}
+
+static void xdg_toplevel_close_handler(void *data,
+        struct xdg_toplevel *toplevel)
+{
+	RGFW_UNUSED(data);
+	RGFW_window* win = (RGFW_window*)xdg_toplevel_get_user_data(toplevel);
+	if (win == NULL)
+		win = RGFW_key_win;
+	
+	RGFW_Event ev;
+	ev.type = RGFW_quit;
+
+	RGFW_eventPipe_push(win, ev); 	
+
+	RGFW_windowQuitCallback(win);
+}
+
+static void shm_format_handler(void *data,
+        struct wl_shm *shm, uint32_t format)
+{
+	RGFW_UNUSED(data); RGFW_UNUSED(shm);
+    fprintf(stderr, "Format %d\n", format);
+}
+
+static const struct wl_shm_listener shm_listener = {
+    .format = shm_format_handler,
+};
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+    .configure = xdg_toplevel_configure_handler,
+    .close = xdg_toplevel_close_handler,
+};
+
+RGFW_window* RGFW_mouse_win = NULL;
+
+static void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface_x); RGFW_UNUSED(surface_y);
+	RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+	RGFW_mouse_win = win;
+
+	RGFW_Event ev;
+	ev.type = RGFW_mouseEnter;
+	ev.point = win->event.point;
+
+	RGFW_eventPipe_push(win, ev); 	
+
+	RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_TRUE);
+}
+static void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface);
+	RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+	if (RGFW_mouse_win == win)
+		RGFW_mouse_win = NULL;
+	
+	RGFW_Event ev;
+	ev.type = RGFW_mouseLeave;
+	ev.point = win->event.point;
+	RGFW_eventPipe_push(win, ev);
+
+	RGFW_mouseNotifyCallBack(win,  win->event.point, RGFW_FALSE);
+}
+static void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(x); RGFW_UNUSED(y);
+
+	assert(RGFW_mouse_win != NULL);
+	
+	RGFW_Event ev;
+	ev.type = RGFW_mousePosChanged;
+	ev.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y));
+	RGFW_eventPipe_push(RGFW_mouse_win, ev);
+	
+	RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)));
+}
+static void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial);
+	assert(RGFW_mouse_win != NULL);
+
+	u32 b = (button - 0x110) + 1;
+
+	/* flip right and middle button codes */
+	if (b == 2) b = 3;
+	else if (b == 3) b = 2;
+	
+	RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current;
+	RGFW_mouseButtons[b].current = state;
+
+	RGFW_Event ev;
+	ev.type = RGFW_mouseButtonPressed + state;
+	ev.button = b;
+	RGFW_eventPipe_push(RGFW_mouse_win, ev);
+
+	RGFW_mouseButtonCallback(RGFW_mouse_win, b, 0, state);
+}
+static void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time);  RGFW_UNUSED(axis);
+	assert(RGFW_mouse_win != NULL); 
+
+	double scroll = wl_fixed_to_double(value);
+
+	RGFW_Event ev;
+	ev.type = RGFW_mouseButtonPressed;
+	ev.button = RGFW_mouseScrollUp + (scroll < 0);
+	RGFW_eventPipe_push(RGFW_mouse_win, ev);
+
+	RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1);
+}
+
+void RGFW_doNothing(void) { }
+static struct wl_pointer_listener pointer_listener = (struct wl_pointer_listener){&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing};
+
+static void keyboard_keymap (void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) {
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(format);
+
+	char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+	xkb_keymap_unref (keymap);
+	keymap = xkb_keymap_new_from_string (xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
+	
+	munmap (keymap_string, size);
+	close (fd);
+	xkb_state_unref (xkb_state);
+	xkb_state = xkb_state_new (keymap);
+}
+static void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { 
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(keys);
+
+	RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface);
+
+	RGFW_Event ev;
+	ev.type = RGFW_focusIn;
+	ev.inFocus = RGFW_TRUE;
+	RGFW_key_win->event.inFocus = RGFW_TRUE;
+
+	RGFW_eventPipe_push((RGFW_window*)RGFW_mouse_win, ev);
+
+	RGFW_focusCallback(RGFW_key_win, RGFW_TRUE);
+}
+static void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { 
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial);
+
+	RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+	if (RGFW_key_win == win)
+		RGFW_key_win = NULL;	
+
+	RGFW_Event ev;
+	ev.type = RGFW_focusOut;
+	ev.inFocus = RGFW_FALSE;
+	win->event.inFocus = RGFW_FALSE;
+	RGFW_eventPipe_push(win, ev);
+
+	RGFW_focusCallback(win, RGFW_FALSE);
+}
+static void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); 
+
+	assert(RGFW_key_win != NULL);
+
+	xkb_keysym_t keysym = xkb_state_key_get_one_sym (xkb_state, key+8);
+	char name[16];
+	xkb_keysym_get_name(keysym, name, 16);
+
+	u32 RGFW_key = RGFW_apiKeyCodeToRGFW(key);
+	RGFW_keyboard[RGFW_key].prev = RGFW_keyboard[RGFW_key].current;
+	RGFW_keyboard[RGFW_key].current = state;
+	RGFW_Event ev;
+	ev.type = RGFW_keyPressed + state;
+	ev.keyCode = RGFW_key;
+	strcpy(ev.keyName, name);
+	ev.repeat = RGFW_isHeld(RGFW_key_win, RGFW_key);
+	RGFW_eventPipe_push(RGFW_key_win, ev);
+	
+	RGFW_updateLockState(RGFW_key_win, xkb_keymap_mod_get_index(keymap, "Lock"), xkb_keymap_mod_get_index(keymap, "Mod2"));
+
+	RGFW_keyCallback(RGFW_key_win, RGFW_key, name, RGFW_key_win->event.lockState, state);
+}
+static void keyboard_modifiers (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); 
+	xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
+}
+static struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void*)&RGFW_doNothing};
+
+static void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) {
+	RGFW_UNUSED(data);
+
+	if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
+		struct wl_pointer *pointer = wl_seat_get_pointer (seat);
+		wl_pointer_add_listener (pointer, &pointer_listener, NULL);
+	}
+	if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
+		struct wl_keyboard *keyboard = wl_seat_get_keyboard (seat);
+		wl_keyboard_add_listener (keyboard, &keyboard_listener, NULL);
+	}
+}
+static struct wl_seat_listener seat_listener = {&seat_capabilities, (void*)&RGFW_doNothing};
+
+static void wl_global_registry_handler(void *data,
+		struct wl_registry *registry, uint32_t id, const char *interface,
+		uint32_t version)
+{
+	RGFW_UNUSED(data); RGFW_UNUSED(version);
+
+    if (strcmp(interface, "wl_compositor") == 0) {
+		RGFW_compositor = wl_registry_bind(registry,
+			id, &wl_compositor_interface, 4);
+	} else if (strcmp(interface, "xdg_wm_base") == 0) {
+	xdg_wm_base = wl_registry_bind(registry,
+		id, &xdg_wm_base_interface, 1);
+	} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
+		decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);
+    } else if (strcmp(interface, "wl_shm") == 0) {
+        shm = wl_registry_bind(registry,
+            id, &wl_shm_interface, 1);
+        wl_shm_add_listener(shm, &shm_listener, NULL);
+	} else if (strcmp(interface,"wl_seat") == 0) {
+		seat = wl_registry_bind(registry, id, &wl_seat_interface, 1);
+		wl_seat_add_listener(seat, &seat_listener, NULL);
+	}
+
+	else {
+		#ifdef RGFW_DEBUG
+		printf("did not register %s\n", interface);
+		return;
+		#endif
+	}
+
+		#ifdef RGFW_DEBUG
+		printf("registered %s\n", interface);
+		#endif
+}
+
+static void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); }
+static const struct wl_registry_listener registry_listener = {
+	.global = wl_global_registry_handler,
+	.global_remove = wl_global_registry_remove,
+};
+
+static const char *get_mode_name(enum zxdg_toplevel_decoration_v1_mode mode) {
+	switch (mode) {
+	case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE:
+		return "client-side decorations";
+	case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE:
+		return "server-side decorations";
+	}
+	abort();
+}
+
+
+static void decoration_handle_configure(void *data,
+		struct zxdg_toplevel_decoration_v1 *decoration,
+		enum zxdg_toplevel_decoration_v1_mode mode) {
+	RGFW_UNUSED(data); RGFW_UNUSED(decoration);
+	printf("Using %s\n", get_mode_name(mode));
+	RGFW_current_mode = mode;
+}
+
+static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = {
+	.configure = decoration_handle_configure,
+};
+
+static void randname(char *buf) {
+	struct timespec ts;
+	clock_gettime(CLOCK_REALTIME, &ts);
+	long r = ts.tv_nsec;
+	for (int i = 0; i < 6; ++i) {
+		buf[i] = 'A'+(r&15)+(r&16)*2;
+		r >>= 5;
+	}
+}
+
+static int anonymous_shm_open(void) {
+	char name[] = "/RGFW-wayland-XXXXXX";
+	int retries = 100;
+
+	do {
+		randname(name + strlen(name) - 6);
+
+		--retries;
+		// shm_open guarantees that O_CLOEXEC is set
+		int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+		if (fd >= 0) {
+			shm_unlink(name);
+			return fd;
+		}
+	} while (retries > 0 && errno == EEXIST);
+
+	return -1;
+}
+
+int create_shm_file(off_t size) {
+	int fd = anonymous_shm_open();
+	if (fd < 0) {
+		return fd;
+	}
+
+	if (ftruncate(fd, size) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) {
+	#ifdef RGFW_BUFFER
+		RGFW_window* win = (RGFW_window*)data;
+		if ((win->_winArgs & RGFW_NO_CPU_RENDER))
+			return;	
+		
+		#ifndef RGFW_X11_DONT_CONVERT_BGR
+			u32 x, y;
+			for (y = 0; y < (u32)win->r.h; y++) {
+				for (x = 0; x < (u32)win->r.w; x++) {
+					u32 index = (y * 4 * win->r.w) + x * 4;
+
+					u8 red = win->buffer[index];
+					win->buffer[index] = win->buffer[index + 2];
+					win->buffer[index + 2] = red;
+
+				}
+			}
+		#endif	
+	
+		wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0);
+		wl_surface_damage_buffer(win->src.surface, 0, 0, win->r.w, win->r.h);
+		wl_surface_commit(win->src.surface);
+	#endif
+}
+
+static const struct wl_callback_listener wl_surface_frame_listener = {
+	.done = wl_surface_frame_done,
+};
+
+
+	/* normal wayland RGFW stuff */
+	
+	RGFW_area RGFW_getScreenSize(void) {
+		RGFW_area area = {};
+
+		if (RGFW_root != NULL)
+			/* this isn't right but it's here for buffers */
+			area = RGFW_AREA(RGFW_root->r.w, RGFW_root->r.h);
+		
+		/* TODO wayland */
+		return area;
+	}
+	
+	void RGFW_releaseCursor(RGFW_window* win) {
+		RGFW_UNUSED(win);
+	}
+
+	void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) {
+		RGFW_UNUSED(win); RGFW_UNUSED(r);
+
+		/* TODO wayland */
+	}
+
+
+	RGFWDEF void RGFW_init_buffer(RGFW_window* win);
+	void RGFW_init_buffer(RGFW_window* win) {
+		#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)	
+			size_t size = win->r.w * win->r.h * 4;
+			int fd = create_shm_file(size);
+			if (fd < 0) {
+				fprintf(stderr, "Failed to create a buffer. size: %ld\n", size);
+				exit(1);
+			}
+
+			win->buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+			if (win->buffer == MAP_FAILED) {
+				fprintf(stderr, "mmap failed!\n");
+				close(fd);
+				exit(1);
+			}
+
+			struct wl_shm_pool* pool = wl_shm_create_pool(shm, fd, size);
+			win->src.wl_buffer = wl_shm_pool_create_buffer(pool, 0, win->r.w, win->r.h, win->r.w * 4,
+				WL_SHM_FORMAT_ARGB8888);
+			wl_shm_pool_destroy(pool);
+
+			close(fd);
+			
+			wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0);
+			wl_surface_commit(win->src.surface);
+
+			u8 color[] = {0x00, 0x00, 0x00, 0xFF};
+
+			size_t i;
+			for (i = 0; i < size; i += 4) {
+				memcpy(&win->buffer[i], color, 4);
+			}
+	
+			#if defined(RGFW_OSMESA)
+					win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
+					OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
+			#endif
+		#else
+		RGFW_UNUSED(win);
+		#endif
+	}
+   
+
+	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
+		RGFW_window* win = RGFW_window_basic_init(rect, args);
+		
+		fprintf(stderr, "Warning: RGFW Wayland support is experimental\n");
+		
+		win->src.display = wl_display_connect(NULL);
+		if (win->src.display == NULL) {
+			#ifdef RGFW_DEBUG
+				fprintf(stderr, "Failed to load Wayland display\n");
+			#endif
+			return NULL;
+		}
+		
+		struct wl_registry *registry = wl_display_get_registry(win->src.display);
+		wl_registry_add_listener(registry, &registry_listener, NULL);
+			
+		wl_display_dispatch(win->src.display);
+		wl_display_roundtrip(win->src.display);
+
+		if (RGFW_compositor == NULL) {
+			#ifdef RGFW_DEBUG
+				fprintf(stderr, "Can't find compositor.\n");
+			#endif
+			
+			return NULL;
+		}
+		
+		if (RGFW_wl_cursor_theme == NULL) {
+			RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, shm);
+			RGFW_cursor_surface = wl_compositor_create_surface(RGFW_compositor); 
+			
+			struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, "left_ptr");
+			RGFW_cursor_image = cursor->images[0];
+			struct wl_buffer* cursor_buffer	= wl_cursor_image_get_buffer(RGFW_cursor_image);
+
+			wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
+			wl_surface_commit(RGFW_cursor_surface); 
+		}
+
+		if (RGFW_root == NULL)
+			xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL);
+		
+		xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+
+		win->src.surface = wl_compositor_create_surface(RGFW_compositor);
+		wl_surface_set_user_data(win->src.surface, win);
+
+		win->src.xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, win->src.surface);
+		xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL);
+	
+		xdg_wm_base_set_user_data(xdg_wm_base, win);
+
+		win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface);
+		xdg_toplevel_set_user_data(win->src.xdg_toplevel, win);
+		xdg_toplevel_set_title(win->src.xdg_toplevel, name);
+		xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, NULL);
+
+		xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h);
+		
+		if (!(args & RGFW_NO_BORDER)) {
+			win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
+						decoration_manager, win->src.xdg_toplevel);
+		}
+
+		if (args & RGFW_CENTER) {
+			RGFW_area screenR = RGFW_getScreenSize();
+			RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2));
+		}
+
+		if (args & RGFW_OPENGL_SOFTWARE)
+			setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
+
+		wl_display_roundtrip(win->src.display);
+
+		wl_surface_commit(win->src.surface);
+		
+		/* wait for the surface to be configured */
+		while (wl_display_dispatch(win->src.display) != -1 && !RGFW_wl_configured) { }
+		
+		
+		#ifdef RGFW_OPENGL
+			if ((args & RGFW_NO_INIT_API) == 0) {
+				win->src.window = wl_egl_window_create(win->src.surface, win->r.w, win->r.h);
+				RGFW_createOpenGLContext(win);
+			}
+		#endif	
+
+		RGFW_init_buffer(win);
+
+		struct wl_callback* callback = wl_surface_frame(win->src.surface);
+   		wl_callback_add_listener(callback, &wl_surface_frame_listener, win);	
+		wl_surface_commit(win->src.surface);
+
+		if (args & RGFW_HIDE_MOUSE) {
+			RGFW_window_showMouse(win, 0);
+		}
+		
+		if (RGFW_root == NULL) {
+			RGFW_root = win;
+		}
+		
+		win->src.eventIndex = 0;
+		win->src.eventLen = 0;
+		
+		return win;
+	}
+
+	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
+		if (win->_winArgs & RGFW_WINDOW_HIDE)
+			return NULL;
+
+		if (win->src.eventIndex == 0) {
+			if (wl_display_roundtrip(win->src.display) == -1) {
+				return NULL;
+			}
+			RGFW_resetKey();
+		}
+
+		#ifdef __linux__
+			RGFW_Event* event = RGFW_linux_updateJoystick(win);
+			if (event != NULL)
+				return event;
+		#endif
+		
+		if (win->src.eventLen == 0) {
+				return NULL;
+		}
+
+		RGFW_Event ev = RGFW_eventPipe_pop(win);
+		
+		if (ev.type ==  0 || win->event.type == RGFW_quit) {
+			return NULL;
+		}
+        
+		ev.frameTime = win->event.frameTime;
+        ev.frameTime2 = win->event.frameTime2;
+        ev.inFocus = win->event.inFocus;
+        win->event = ev;
+		
+		return &win->event;
+	}
+
+
+	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
+		RGFW_UNUSED(win); RGFW_UNUSED(a);
+
+		/* TODO wayland */
+	}
+
+	void RGFW_window_move(RGFW_window* win, RGFW_point v) {
+		RGFW_UNUSED(win); RGFW_UNUSED(v);
+
+		/* TODO wayland */
+	}
+
+	void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) {
+		RGFW_UNUSED(win); RGFW_UNUSED(src); RGFW_UNUSED(a); RGFW_UNUSED(channels)
+		/* TODO wayland */
+	}
+
+	void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) {
+		RGFW_UNUSED(win); RGFW_UNUSED(v);
+
+		/* TODO wayland */
+	}
+
+	void RGFW_window_showMouse(RGFW_window* win, i8 show) {
+		RGFW_UNUSED(win);
+
+		if (show) {
+
+		}
+		else {
+			
+		}
+
+		/* TODO wayland */
+	}
+
+	b8 RGFW_window_isMaximized(RGFW_window* win) {
+		RGFW_UNUSED(win);
+		/* TODO wayland */
+		return 0;
+	}
+
+	b8 RGFW_window_isMinimized(RGFW_window* win) {
+		RGFW_UNUSED(win);
+		/* TODO wayland */
+		return 0;
+	}
+
+	b8 RGFW_window_isHidden(RGFW_window* win) {
+		RGFW_UNUSED(win);
+		/* TODO wayland */
+		return 0;
+	}
+
+	b8 RGFW_window_isFullscreen(RGFW_window* win) {
+		RGFW_UNUSED(win);
+		/* TODO wayland */
+		return 0;
+	}
+
+	RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
+		RGFW_UNUSED(win);
+		/* TODO wayland */
+		return RGFW_POINT(0, 0);
+	}
+			
+	RGFW_point RGFW_getGlobalMousePoint(void) {
+		/* TODO wayland */
+		return RGFW_POINT(0, 0);
+	}
+			
+	void RGFW_window_show(RGFW_window* win) {
+		//wl_surface_attach(win->src.surface, win->rc., 0, 0);
+        wl_surface_commit(win->src.surface);
+		
+		if (win->_winArgs & RGFW_WINDOW_HIDE)
+			win->_winArgs ^= RGFW_WINDOW_HIDE;
+	}
+		
+	void RGFW_window_hide(RGFW_window* win) {
+		wl_surface_attach(win->src.surface, NULL, 0, 0);
+        wl_surface_commit(win->src.surface);
+		win->_winArgs |= RGFW_WINDOW_HIDE;
+	}
+		
+	void RGFW_window_setMouseDefault(RGFW_window* win) {
+		RGFW_UNUSED(win);
+	
+		RGFW_window_setMouseStandard(win, RGFW_MOUSE_NORMAL);
+	}
+		
+	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
+		RGFW_UNUSED(win);
+		
+		static const char* iconStrings[] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" };
+
+		struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]);
+		RGFW_cursor_image = cursor->images[0];
+		struct wl_buffer* cursor_buffer	= wl_cursor_image_get_buffer(RGFW_cursor_image);
+
+		wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
+		wl_surface_commit(RGFW_cursor_surface); 
+	}
+		
+	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
+		RGFW_UNUSED(win); RGFW_UNUSED(image); RGFW_UNUSED(a); RGFW_UNUSED(channels)
+		//struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]);
+		//RGFW_cursor_image = image;
+		struct wl_buffer* cursor_buffer	= wl_cursor_image_get_buffer(RGFW_cursor_image);
+
+		wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
+		wl_surface_commit(RGFW_cursor_surface); 
+	}
+		
+	void RGFW_window_setName(RGFW_window* win, char* name) {
+		xdg_toplevel_set_title(win->src.xdg_toplevel, name);
+	}
+		
+	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
+		RGFW_UNUSED(win); RGFW_UNUSED(passthrough);
+
+	/* TODO wayland */
+	}
+		
+	void RGFW_window_setBorder(RGFW_window* win, b8 border) {
+		RGFW_UNUSED(win); RGFW_UNUSED(border);
+
+	/* TODO wayland */
+	}
+		
+	void RGFW_window_restore(RGFW_window* win) {
+		RGFW_UNUSED(win);
+
+	/* TODO wayland */
+	}
+		
+	void RGFW_window_minimize(RGFW_window* win) {
+		RGFW_UNUSED(win);
+
+	/* TODO wayland */
+	}
+		
+	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
+		RGFW_UNUSED(win); RGFW_UNUSED(a);
+
+	/* TODO wayland */
+	}
+		
+	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
+		RGFW_UNUSED(win); RGFW_UNUSED(a);
+
+	/* TODO wayland */
+	}
+
+	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
+		RGFW_monitor m = {};
+		RGFW_UNUSED(win);
+		RGFW_UNUSED(m);
+		/* TODO wayland */
+
+		return m;
+	}
+
+
+	#ifndef RGFW_EGL
+	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); }
+	#endif
+
+	void RGFW_window_swapBuffers(RGFW_window* win) {
+		assert(win != NULL);
+
+		/* clear the window*/
+		#ifdef RGFW_BUFFER	
+			wl_surface_frame_done(win, NULL, 0);
+			if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) 
+		#endif
+		{
+		#ifdef RGFW_OPENGL
+			eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
+		#endif
+		}
+		
+		wl_display_flush(win->src.display);
+	}
+
+	void RGFW_window_close(RGFW_window* win) {
+		#ifdef RGFW_EGL
+			RGFW_closeEGL(win);
+		#endif
+
+		if (RGFW_root == win) {
+			RGFW_root = NULL;
+		}
+
+		xdg_toplevel_destroy(win->src.xdg_toplevel);
+		xdg_surface_destroy(win->src.xdg_surface);
+		wl_surface_destroy(win->src.surface);
+
+		#ifdef RGFW_BUFFER
+		wl_buffer_destroy(win->src.wl_buffer);
+		#endif
+		
+		wl_display_disconnect(win->src.display);
+		RGFW_FREE(win);
+	}
+
+	RGFW_monitor RGFW_getPrimaryMonitor(void) {
+		/* TODO wayland */
+
+		return (RGFW_monitor){};
+	}
+						
+	RGFW_monitor* RGFW_getMonitors(void) {
+		/* TODO wayland */
+
+		return NULL;
+	}
+
+	void RGFW_writeClipboard(const char* text, u32 textLen) {
+		RGFW_UNUSED(text); RGFW_UNUSED(textLen);
+
+		/* TODO wayland */
+	}
+
+	char* RGFW_readClipboard(size_t* size) {
+		RGFW_UNUSED(size);
+
+		/* TODO wayland */
+
+		return NULL;
+	}
+#endif /* RGFW_WAYLAND */
+
+/* 
+	End of Wayland defines
+*/
+
+
+/*
+
+	Start of Windows defines
+
+
+*/
+
+#ifdef RGFW_WINDOWS
+	#define WIN32_LEAN_AND_MEAN
+	#define OEMRESOURCE
+	#include <windows.h>
+	
+	#include <processthreadsapi.h>
+	#include <wchar.h>
+	#include <locale.h>
+	#include <windowsx.h>
+	#include <shellapi.h>
+	#include <shellscalingapi.h>
+
+	#include <winuser.h>
+
+	__declspec(dllimport) int __stdcall WideCharToMultiByte( UINT CodePage, DWORD dwFlags, const WCHAR* lpWideCharStr, int cchWideChar,  LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);
+	
+	#ifndef RGFW_NO_XINPUT
+	typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*);
+	PFN_XInputGetState XInputGetStateSRC = NULL;
+	#define XInputGetState XInputGetStateSRC
+
+	typedef DWORD (WINAPI * PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE);
+	PFN_XInputGetKeystroke XInputGetKeystrokeSRC = NULL;
+	#define XInputGetKeystroke XInputGetKeystrokeSRC
+
+	static HMODULE RGFW_XInput_dll = NULL;
+	#endif
+
+	u32 RGFW_mouseIconSrc[] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO};
+
+	char* createUTF8FromWideStringWin32(const WCHAR* source);
+
+#define GL_FRONT				0x0404
+#define GL_BACK					0x0405
+#define GL_LEFT					0x0406
+#define GL_RIGHT				0x0407
+
+#if defined(RGFW_OSMESA) && defined(RGFW_LINK_OSMESA)
+
+	typedef void (GLAPIENTRY* PFN_OSMesaDestroyContext)(OSMesaContext);
+	typedef i32(GLAPIENTRY* PFN_OSMesaMakeCurrent)(OSMesaContext, void*, int, int, int);
+	typedef OSMesaContext(GLAPIENTRY* PFN_OSMesaCreateContext)(GLenum, OSMesaContext);
+
+	PFN_OSMesaMakeCurrent OSMesaMakeCurrentSource;
+	PFN_OSMesaCreateContext OSMesaCreateContextSource;
+	PFN_OSMesaDestroyContext OSMesaDestroyContextSource;
+
+#define OSMesaCreateContext OSMesaCreateContextSource
+#define OSMesaMakeCurrent OSMesaMakeCurrentSource
+#define OSMesaDestroyContext OSMesaDestroyContextSource
+#endif
+
+	typedef int (*PFN_wglGetSwapIntervalEXT)(void);
+	PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL;
+#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc
+
+
+	void* RGFWjoystickApi = NULL;
+
+	/* these two wgl functions need to be preloaded */
+	typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);
+	PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
+
+	/* defines for creating ARB attributes */
+#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
+#define WGL_CONTEXT_MAJOR_VERSION_ARB             0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB             0x2092
+#define WGL_DRAW_TO_WINDOW_ARB                    0x2001
+#define WGL_ACCELERATION_ARB                      0x2003
+#define WGL_NO_ACCELERATION_ARB 0x2025
+#define WGL_DOUBLE_BUFFER_ARB                     0x2011
+#define WGL_COLOR_BITS_ARB                        0x2014
+#define WGL_RED_BITS_ARB 0x2015
+#define WGL_RED_SHIFT_ARB 0x2016
+#define WGL_GREEN_BITS_ARB 0x2017
+#define WGL_GREEN_SHIFT_ARB 0x2018
+#define WGL_BLUE_BITS_ARB 0x2019
+#define WGL_BLUE_SHIFT_ARB 0x201a
+#define WGL_ALPHA_BITS_ARB 0x201b
+#define WGL_ALPHA_SHIFT_ARB 0x201c
+#define WGL_ACCUM_BITS_ARB 0x201d
+#define WGL_ACCUM_RED_BITS_ARB 0x201e
+#define WGL_ACCUM_GREEN_BITS_ARB 0x201f
+#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
+#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
+#define WGL_DEPTH_BITS_ARB 0x2022
+#define WGL_AUX_BUFFERS_ARB 0x2024
+#define WGL_STEREO_ARB 0x2012
+#define WGL_DEPTH_BITS_ARB                        0x2022
+#define WGL_STENCIL_BITS_ARB 					  0x2023
+#define WGL_FULL_ACCELERATION_ARB                 0x2027
+#define WGL_CONTEXT_FLAGS_ARB                     0x2094
+#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126
+#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define WGL_SAMPLE_BUFFERS_ARB               0x2041
+#define WGL_SAMPLES_ARB 0x2042
+#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
+
+#ifndef RGFW_EGL
+static HMODULE wglinstance = NULL;
+#endif
+
+#ifdef RGFW_WGL_LOAD
+	typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC);
+	typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC);
+	typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR);
+	typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC);
+	typedef HDC(WINAPI* PFN_wglGetCurrentDC)();
+	typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)();
+
+	PFN_wglCreateContext wglCreateContextSRC;
+	PFN_wglDeleteContext wglDeleteContextSRC;
+	PFN_wglGetProcAddress wglGetProcAddressSRC;
+	PFN_wglMakeCurrent wglMakeCurrentSRC;
+	PFN_wglGetCurrentDC wglGetCurrentDCSRC;
+	PFN_wglGetCurrentContext wglGetCurrentContextSRC;
+
+	#define wglCreateContext wglCreateContextSRC
+	#define wglDeleteContext wglDeleteContextSRC
+	#define wglGetProcAddress wglGetProcAddressSRC
+	#define wglMakeCurrent wglMakeCurrentSRC
+
+	#define wglGetCurrentDC wglGetCurrentDCSRC
+	#define wglGetCurrentContext wglGetCurrentContextSRC
+#endif
+
+#ifdef RGFW_OPENGL
+	void* RGFW_getProcAddress(const char* procname) { 
+		void* proc = (void*) wglGetProcAddress(procname);
+		if (proc)
+			return proc;
+
+		return (void*) GetProcAddress(wglinstance, procname); 
+	}
+
+	typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
+	static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
+#endif
+
+	RGFW_window RGFW_eventWindow;
+
+	LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+		switch (message) {
+		case WM_MOVE:
+			RGFW_eventWindow.r.x = LOWORD(lParam);
+			RGFW_eventWindow.r.y = HIWORD(lParam);
+			RGFW_eventWindow.src.window = hWnd;
+			return DefWindowProcA(hWnd, message, wParam, lParam);
+		case WM_SIZE:
+			RGFW_eventWindow.r.w = LOWORD(lParam);
+			RGFW_eventWindow.r.h = HIWORD(lParam);
+			RGFW_eventWindow.src.window = hWnd;
+			return DefWindowProcA(hWnd, message, wParam, lParam); // Call DefWindowProc after handling
+		default:
+			return DefWindowProcA(hWnd, message, wParam, lParam);
+		}
+	}
+	
+	#ifndef RGFW_NO_DPI
+	static HMODULE RGFW_Shcore_dll = NULL;
+	typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*);
+	PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL;
+	#define GetDpiForMonitor GetDpiForMonitorSRC
+	#endif
+
+	__declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod);
+	
+	#ifndef RGFW_NO_XINPUT
+	void RGFW_loadXInput(void) {
+		u32 i;
+		static const char* names[] = { 
+			"xinput1_4.dll",
+			"xinput1_3.dll",
+			"xinput9_1_0.dll",
+			"xinput1_2.dll",
+			"xinput1_1.dll"
+		};
+
+		for (i = 0; i < sizeof(names) / sizeof(const char*);  i++) {
+			RGFW_XInput_dll = LoadLibraryA(names[i]);
+
+			if (RGFW_XInput_dll) {
+				XInputGetStateSRC = (PFN_XInputGetState)(void*)GetProcAddress(RGFW_XInput_dll, "XInputGetState");
+			
+				if (XInputGetStateSRC == NULL)
+					printf("Failed to load XInputGetState");
+			}
+		}
+	}
+	#endif
+
+	RGFWDEF void RGFW_init_buffer(RGFW_window* win);
+	void RGFW_init_buffer(RGFW_window* win) {
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+	if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
+		RGFW_bufferSize = RGFW_getScreenSize();
+	
+	BITMAPV5HEADER bi = { 0 };
+	ZeroMemory(&bi, sizeof(bi));
+	bi.bV5Size = sizeof(bi);
+	bi.bV5Width = RGFW_bufferSize.w;
+	bi.bV5Height = -((LONG) RGFW_bufferSize.h);
+	bi.bV5Planes = 1;
+	bi.bV5BitCount = 32;
+	bi.bV5Compression = BI_BITFIELDS;
+	bi.bV5BlueMask = 0x00ff0000;
+	bi.bV5GreenMask = 0x0000ff00;
+	bi.bV5RedMask = 0x000000ff;
+	bi.bV5AlphaMask = 0xff000000;
+
+	win->src.bitmap = CreateDIBSection(win->src.hdc,
+		(BITMAPINFO*) &bi,
+		DIB_RGB_COLORS,
+		(void**) &win->buffer,
+		NULL,
+		(DWORD) 0);
+	
+	win->src.hdcMem = CreateCompatibleDC(win->src.hdc);
+
+	#if defined(RGFW_OSMESA)
+	win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
+	OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
+	#endif
+#else
+RGFW_UNUSED(win); /*!< if buffer rendering is not being used */
+#endif
+	}
+
+	void RGFW_window_setDND(RGFW_window* win, b8 allow) {
+		DragAcceptFiles(win->src.window, allow);
+	}
+
+	void RGFW_releaseCursor(RGFW_window* win) {
+		RGFW_UNUSED(win);
+		ClipCursor(NULL);
+    	const RAWINPUTDEVICE id = { 0x01, 0x02, RIDEV_REMOVE, NULL };
+    	RegisterRawInputDevices(&id, 1, sizeof(id));	
+	}
+
+	void RGFW_captureCursor(RGFW_window* win, RGFW_rect rect) {
+		RGFW_UNUSED(win); RGFW_UNUSED(rect);
+
+		RECT clipRect;
+		GetClientRect(win->src.window, &clipRect);
+		ClientToScreen(win->src.window, (POINT*) &clipRect.left);
+		ClientToScreen(win->src.window, (POINT*) &clipRect.right);
+		ClipCursor(&clipRect);
+
+	    const RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window };
+		RegisterRawInputDevices(&id, 1, sizeof(id));
+	}
+
+	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
+		#ifndef RGFW_NO_XINPUT
+		if (RGFW_XInput_dll == NULL)
+			RGFW_loadXInput();
+		#endif
+
+		#ifndef RGFW_NO_DPI
+		if (RGFW_Shcore_dll == NULL) {
+			RGFW_Shcore_dll = LoadLibraryA("shcore.dll");
+			GetDpiForMonitorSRC = (PFN_GetDpiForMonitor)(void*)GetProcAddress(RGFW_Shcore_dll, "GetDpiForMonitor");
+			SetProcessDPIAware();
+		}
+		#endif
+
+		if (wglinstance == NULL) {
+			wglinstance = LoadLibraryA("opengl32.dll");
+#ifdef RGFW_WGL_LOAD
+			wglCreateContextSRC = (PFN_wglCreateContext) GetProcAddress(wglinstance, "wglCreateContext");
+			wglDeleteContextSRC = (PFN_wglDeleteContext) GetProcAddress(wglinstance, "wglDeleteContext");
+			wglGetProcAddressSRC = (PFN_wglGetProcAddress) GetProcAddress(wglinstance, "wglGetProcAddress");
+			wglMakeCurrentSRC = (PFN_wglMakeCurrent) GetProcAddress(wglinstance, "wglMakeCurrent");
+			wglGetCurrentDCSRC = (PFN_wglGetCurrentDC) GetProcAddress(wglinstance, "wglGetCurrentDC");
+			wglGetCurrentContextSRC = (PFN_wglGetCurrentContext) GetProcAddress(wglinstance, "wglGetCurrentContext");
+#endif
+		}
+	
+		if (name[0] == 0) name = (char*) " ";
+
+		RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1);
+		RGFW_eventWindow.src.window = NULL;
+
+		RGFW_window* win = RGFW_window_basic_init(rect, args);
+
+		win->src.maxSize = RGFW_AREA(0, 0);
+		win->src.minSize = RGFW_AREA(0, 0);
+
+
+		HINSTANCE inh = GetModuleHandleA(NULL);
+
+		#ifndef __cplusplus
+		WNDCLASSA Class = { 0 }; /*!< Setup the Window class. */
+		#else
+		WNDCLASSA Class = { };
+		#endif
+
+		if (RGFW_className == NULL)
+			RGFW_className = (char*)name;
+
+		Class.lpszClassName = RGFW_className;
+		Class.hInstance = inh;
+		Class.hCursor = LoadCursor(NULL, IDC_ARROW);
+		Class.lpfnWndProc = WndProc;
+
+		RegisterClassA(&Class);
+
+		DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
+
+		RECT windowRect, clientRect;
+
+		if (!(args & RGFW_NO_BORDER)) {
+			window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX;
+
+			if (!(args & RGFW_NO_RESIZE))
+				window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME;
+		} else
+			window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX;
+
+		HWND dummyWin = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0);
+
+		GetWindowRect(dummyWin, &windowRect);
+		GetClientRect(dummyWin, &clientRect);
+
+		win->src.hOffset = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
+		win->src.window = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + win->src.hOffset, 0, 0, inh, 0);
+
+		if (args & RGFW_ALLOW_DND) {
+			win->_winArgs |= RGFW_ALLOW_DND;
+			RGFW_window_setDND(win, 1);
+		}
+		win->src.hdc = GetDC(win->src.window);
+
+		if ((args & RGFW_NO_INIT_API) == 0) {
+#ifdef RGFW_DIRECTX
+		assert(FAILED(CreateDXGIFactory(&__uuidof(IDXGIFactory), (void**) &RGFW_dxInfo.pFactory)) == 0);
+
+		if (FAILED(RGFW_dxInfo.pFactory->lpVtbl->EnumAdapters(RGFW_dxInfo.pFactory, 0, &RGFW_dxInfo.pAdapter))) {
+			fprintf(stderr, "Failed to enumerate DXGI adapters\n");
+			RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory);
+			return NULL;
+		}
+
+		D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 };
+
+		if (FAILED(D3D11CreateDevice(RGFW_dxInfo.pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &RGFW_dxInfo.pDevice, NULL, &RGFW_dxInfo.pDeviceContext))) {
+			fprintf(stderr, "Failed to create Direct3D device\n");
+			RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter);
+			RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory);
+			return NULL;
+		}
+
+		DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };
+		swapChainDesc.BufferCount = 1;
+		swapChainDesc.BufferDesc.Width = win->r.w;
+		swapChainDesc.BufferDesc.Height = win->r.h;
+		swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+		swapChainDesc.OutputWindow = win->src.window;
+		swapChainDesc.SampleDesc.Count = 1;
+		swapChainDesc.SampleDesc.Quality = 0;
+		swapChainDesc.Windowed = TRUE;
+		RGFW_dxInfo.pFactory->lpVtbl->CreateSwapChain(RGFW_dxInfo.pFactory, (IUnknown*) RGFW_dxInfo.pDevice, &swapChainDesc, &win->src.swapchain);
+
+		ID3D11Texture2D* pBackBuffer;
+		win->src.swapchain->lpVtbl->GetBuffer(win->src.swapchain, 0, &__uuidof(ID3D11Texture2D), (LPVOID*) &pBackBuffer);
+		RGFW_dxInfo.pDevice->lpVtbl->CreateRenderTargetView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pBackBuffer, NULL, &win->src.renderTargetView);
+		pBackBuffer->lpVtbl->Release(pBackBuffer);
+
+		D3D11_TEXTURE2D_DESC depthStencilDesc = { 0 };
+		depthStencilDesc.Width = win->r.w;
+		depthStencilDesc.Height = win->r.h;
+		depthStencilDesc.MipLevels = 1;
+		depthStencilDesc.ArraySize = 1;
+		depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
+		depthStencilDesc.SampleDesc.Count = 1;
+		depthStencilDesc.SampleDesc.Quality = 0;
+		depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
+		depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
+
+		ID3D11Texture2D* pDepthStencilTexture = NULL;
+		RGFW_dxInfo.pDevice->lpVtbl->CreateTexture2D(RGFW_dxInfo.pDevice, &depthStencilDesc, NULL, &pDepthStencilTexture);
+
+		D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc = { 0 };
+		depthStencilViewDesc.Format = depthStencilDesc.Format;
+		depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
+		depthStencilViewDesc.Texture2D.MipSlice = 0;
+
+		RGFW_dxInfo.pDevice->lpVtbl->CreateDepthStencilView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pDepthStencilTexture, &depthStencilViewDesc, &win->src.pDepthStencilView);
+
+		pDepthStencilTexture->lpVtbl->Release(pDepthStencilTexture);
+
+		RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, win->src.pDepthStencilView);
+#endif
+
+#ifdef RGFW_OPENGL 
+		HDC dummy_dc = GetDC(dummyWin);
+        
+        u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; 
+        
+        //if (RGFW_DOUBLE_BUFFER)    
+             pfd_flags |= PFD_DOUBLEBUFFER;
+		
+        PIXELFORMATDESCRIPTOR pfd = {
+			sizeof(pfd),
+			1, /* version */
+			pfd_flags,
+		    PFD_TYPE_RGBA, /* ipixel type */
+			24, /* color bits */
+			0, 0, 0, 0, 0, 0,
+			8, /* alpha bits */
+			0, 0, 0, 0, 0, 0,
+			32, /* depth bits */
+			8, /* stencil bits */ 
+			0,
+			PFD_MAIN_PLANE, /* Layer type */
+			0, 0, 0, 0
+		};
+
+		int pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
+		SetPixelFormat(dummy_dc, pixel_format, &pfd);
+
+		HGLRC dummy_context = wglCreateContext(dummy_dc);
+		wglMakeCurrent(dummy_dc, dummy_context);
+
+		if (wglChoosePixelFormatARB == NULL) {
+			wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB");
+			wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB");
+		}
+
+		wglMakeCurrent(dummy_dc, 0);
+		wglDeleteContext(dummy_context);
+		ReleaseDC(dummyWin, dummy_dc);
+		
+		/* try to create the pixel format we want for opengl and then try to create an opengl context for the specified version */ 
+		if (wglCreateContextAttribsARB != NULL) {
+			PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+			if (args & RGFW_OPENGL_SOFTWARE)
+				pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED;
+
+			if (wglChoosePixelFormatARB != NULL) {
+				i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE);
+
+				int pixel_format;
+				UINT num_formats;
+				wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats);
+				if (!num_formats) {
+					printf("Failed to create a pixel format for WGL.\n");
+				}
+
+				DescribePixelFormat(win->src.hdc, pixel_format, sizeof(pfd), &pfd);
+				if (!SetPixelFormat(win->src.hdc, pixel_format, &pfd)) {
+					printf("Failed to set the WGL pixel format.\n");
+				}
+			}
+			
+			/* create opengl/WGL context for the specified version */ 
+			u32 index = 0;
+			i32 attribs[40];
+
+			if (RGFW_profile == RGFW_GL_CORE) {
+				SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB);
+			}
+			else {
+				SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
+			}
+			
+			if (RGFW_majorVersion || RGFW_minorVersion) {
+				SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion);
+				SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion);
+			}
+
+			SET_ATTRIB(0, 0);
+
+			win->src.ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs);
+		} else { /* fall back to a default context (probably opengl 2 or something) */
+			fprintf(stderr, "Failed to create an accelerated OpenGL Context\n");
+
+			int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd);
+			SetPixelFormat(win->src.hdc, pixel_format, &pfd);
+
+			win->src.ctx = wglCreateContext(win->src.hdc);
+		}
+		
+		wglMakeCurrent(win->src.hdc, win->src.ctx);
+#endif
+	}
+
+#ifdef RGFW_OSMESA
+#ifdef RGFW_LINK_OSM ESA
+		OSMesaMakeCurrentSource = (PFN_OSMesaMakeCurrent) GetProcAddress(win->src.hdc, "OSMesaMakeCurrent");
+		OSMesaCreateContextSource = (PFN_OSMesaCreateContext) GetProcAddress(win->src.hdc, "OSMesaCreateContext");
+		OSMesaDestroyContextSource = (PFN_OSMesaDestroyContext) GetProcAddress(win->src.hdc, "OSMesaDestroyContext");
+#endif
+#endif
+
+#ifdef RGFW_OPENGL
+		if ((args & RGFW_NO_INIT_API) == 0) {
+			ReleaseDC(win->src.window, win->src.hdc);
+			win->src.hdc = GetDC(win->src.window);
+			wglMakeCurrent(win->src.hdc, win->src.ctx);
+		}
+#endif
+
+		DestroyWindow(dummyWin);
+		RGFW_init_buffer(win);
+
+
+		#ifndef RGFW_NO_MONITOR
+		if (args & RGFW_SCALE_TO_MONITOR)
+			RGFW_window_scaleToMonitor(win);
+		#endif
+	
+		if (args & RGFW_CENTER) {
+			RGFW_area screenR = RGFW_getScreenSize();
+			RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2));
+		}
+
+#ifdef RGFW_EGL
+		if ((args & RGFW_NO_INIT_API) == 0)
+			RGFW_createOpenGLContext(win);
+#endif
+
+		if (args & RGFW_HIDE_MOUSE)
+			RGFW_window_showMouse(win, 0);
+
+		if (args & RGFW_TRANSPARENT_WINDOW) {
+			SetWindowLong(win->src.window, GWL_EXSTYLE, GetWindowLong(win->src.window, GWL_EXSTYLE) | WS_EX_LAYERED);
+			SetLayeredWindowAttributes(win->src.window, RGB(255, 255, 255), RGFW_ALPHA, LWA_ALPHA);
+		}
+
+		ShowWindow(win->src.window, SW_SHOWNORMAL);
+		
+		if (RGFW_root == NULL)
+			RGFW_root = win;
+		
+		#ifdef RGFW_OPENGL
+		else 
+			wglShareLists(RGFW_root->src.ctx, win->src.ctx);
+		#endif
+
+		return win;
+	}
+
+	void RGFW_window_setBorder(RGFW_window* win, u8 border) {
+		DWORD style = GetWindowLong(win->src.window, GWL_STYLE);
+
+		if (border == 0) {
+			SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW);
+			SetWindowPos(
+				win->src.window, HWND_TOP, 0, 0, 0, 0,
+				SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
+			);
+		}
+		else {
+			SetWindowLong(win->src.window, GWL_STYLE, style | WS_OVERLAPPEDWINDOW);
+			SetWindowPos(
+				win->src.window, HWND_TOP, 0, 0, 0, 0,
+				SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
+			);
+		}
+	}
+
+
+	RGFW_area RGFW_getScreenSize(void) {
+		return RGFW_AREA(GetDeviceCaps(GetDC(NULL), HORZRES), GetDeviceCaps(GetDC(NULL), VERTRES));
+	}
+
+	RGFW_point RGFW_getGlobalMousePoint(void) {
+		POINT p;
+		GetCursorPos(&p);
+
+		return RGFW_POINT(p.x, p.y);
+	}
+
+	RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
+		POINT p;
+		GetCursorPos(&p);
+		ScreenToClient(win->src.window, &p);
+
+		return RGFW_POINT(p.x, p.y);
+	}
+
+	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
+		assert(win != NULL);
+		win->src.minSize = a;
+	}
+
+	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
+		assert(win != NULL);
+		win->src.maxSize = a;
+	}
+
+
+	void RGFW_window_minimize(RGFW_window* win) {
+		assert(win != NULL);
+
+		ShowWindow(win->src.window, SW_MINIMIZE);
+	}
+
+	void RGFW_window_restore(RGFW_window* win) {
+		assert(win != NULL);
+
+		ShowWindow(win->src.window, SW_RESTORE);
+	}
+
+
+	u8 RGFW_xinput2RGFW[] = {
+		RGFW_JS_A, /* or PS X button */
+		RGFW_JS_B, /* or PS circle button */
+		RGFW_JS_X, /* or PS square button */
+		RGFW_JS_Y, /* or PS triangle button */
+		RGFW_JS_R1, /* right bumper */
+		RGFW_JS_L1, /* left bump */
+		RGFW_JS_L2, /* left trigger*/
+		RGFW_JS_R2, /* right trigger */
+		0, 0, 0, 0, 0, 0, 0, 0,
+		RGFW_JS_UP, /* dpad up */
+		RGFW_JS_DOWN, /* dpad down*/
+		RGFW_JS_LEFT, /* dpad left */
+		RGFW_JS_RIGHT, /* dpad right */
+		RGFW_JS_START, /* start button */
+		RGFW_JS_SELECT/* select button */
+	};
+
+	static i32 RGFW_checkXInput(RGFW_window* win, RGFW_Event* e) {
+		RGFW_UNUSED(win)
+		
+		size_t i;
+		for (i = 0; i < 4; i++) {
+			XINPUT_KEYSTROKE keystroke;
+
+			if (XInputGetKeystroke == NULL)
+				return 0;
+
+			DWORD result = XInputGetKeystroke((DWORD)i, 0, &keystroke);
+
+			if ((keystroke.Flags & XINPUT_KEYSTROKE_REPEAT) == 0 && result != ERROR_EMPTY) {
+				if (result != ERROR_SUCCESS)
+					return 0;
+
+				if (keystroke.VirtualKey > VK_PAD_BACK)
+					continue;
+
+				// RGFW_jsButtonPressed + 1 = RGFW_jsButtonReleased
+				e->type = RGFW_jsButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
+				e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800];
+				RGFW_jsPressed[i][e->button] = !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
+
+				return 1;
+			}
+
+			XINPUT_STATE state;
+			if (XInputGetState == NULL ||
+				XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED
+			)
+				return 0;
+#define INPUT_DEADZONE  ( 0.24f * (float)(0x7FFF) )  // Default to 24% of the +/- 32767 range.   This is a reasonable default value but can be altered if needed.
+
+			if ((state.Gamepad.sThumbLX < INPUT_DEADZONE &&
+				state.Gamepad.sThumbLX > -INPUT_DEADZONE) &&
+				(state.Gamepad.sThumbLY < INPUT_DEADZONE &&
+					state.Gamepad.sThumbLY > -INPUT_DEADZONE))
+			{
+				state.Gamepad.sThumbLX = 0;
+				state.Gamepad.sThumbLY = 0;
+			}
+
+			if ((state.Gamepad.sThumbRX < INPUT_DEADZONE &&
+				state.Gamepad.sThumbRX > -INPUT_DEADZONE) &&
+				(state.Gamepad.sThumbRY < INPUT_DEADZONE &&
+					state.Gamepad.sThumbRY > -INPUT_DEADZONE))
+			{
+				state.Gamepad.sThumbRX = 0;
+				state.Gamepad.sThumbRY = 0;
+			}
+
+			e->axisesCount = 2;
+			RGFW_point axis1 = RGFW_POINT(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY);
+			RGFW_point axis2 = RGFW_POINT(state.Gamepad.sThumbRX, state.Gamepad.sThumbRY);
+
+			if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y || axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) {
+				e->type = RGFW_jsAxisMove;
+
+				e->axis[0] = axis1;
+				e->axis[1] = axis2;
+
+				return 1;
+			}
+
+			e->axis[0] = axis1;
+			e->axis[1] = axis2;
+		}
+
+		return 0;
+	}
+
+	void RGFW_stopCheckEvents(void) { 
+		PostMessageW(RGFW_root->src.window, WM_NULL, 0, 0);
+	}
+
+	void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
+		RGFW_UNUSED(win);
+
+		MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (waitMS * 1e3), QS_ALLINPUT);
+	}
+
+	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
+		assert(win != NULL);
+
+		if (win->event.type == RGFW_quit) {
+			return NULL;
+		}
+
+		MSG msg;
+
+		if (RGFW_eventWindow.src.window == win->src.window) {
+			if (RGFW_eventWindow.r.x != -1) {
+				win->r.x = RGFW_eventWindow.r.x;
+				win->r.y = RGFW_eventWindow.r.y;
+				win->event.type = RGFW_windowMoved;
+				RGFW_windowMoveCallback(win, win->r);
+			}
+
+			if (RGFW_eventWindow.r.w != -1) {
+				win->r.w = RGFW_eventWindow.r.w;
+				win->r.h = RGFW_eventWindow.r.h;
+				win->event.type = RGFW_windowResized;
+				RGFW_windowResizeCallback(win, win->r);
+			}
+
+			RGFW_eventWindow.src.window = NULL;
+			RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1);
+
+			return &win->event;
+		}
+
+
+		static HDROP drop;
+		
+		if (win->event.type == RGFW_dnd_init) {
+			if (win->event.droppedFilesCount) {
+				u32 i;
+				for (i = 0; i < win->event.droppedFilesCount; i++)
+					win->event.droppedFiles[i][0] = '\0';
+			}
+
+			win->event.droppedFilesCount = 0;
+			win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0);
+			//win->event.droppedFiles = (char**)RGFW_CALLOC(win->event.droppedFilesCount, sizeof(char*));
+
+			u32 i;
+			for (i = 0; i < win->event.droppedFilesCount; i++) {
+				const UINT length = DragQueryFileW(drop, i, NULL, 0);
+				WCHAR* buffer = (WCHAR*) RGFW_CALLOC((size_t) length + 1, sizeof(WCHAR));
+
+				DragQueryFileW(drop, i, buffer, length + 1);
+				strncpy(win->event.droppedFiles[i], createUTF8FromWideStringWin32(buffer), RGFW_MAX_PATH);
+				win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0';
+				RGFW_FREE(buffer);
+			}
+
+			DragFinish(drop);
+			RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
+			
+			win->event.type = RGFW_dnd;
+			return &win->event;
+		}
+
+		win->event.inFocus = (GetForegroundWindow() == win->src.window);
+
+		if (RGFW_checkXInput(win, &win->event))
+			return &win->event;
+
+		static BYTE keyboardState[256];
+
+		if (PeekMessageA(&msg, win->src.window, 0u, 0u, PM_REMOVE)) {
+			switch (msg.message) {
+			case WM_CLOSE:
+			case WM_QUIT:
+				RGFW_windowQuitCallback(win);
+				win->event.type = RGFW_quit;
+				break;
+
+			case WM_ACTIVATE:
+				win->event.inFocus = (LOWORD(msg.wParam) == WA_INACTIVE);
+
+				if (win->event.inFocus) {
+					win->event.type = RGFW_focusIn;
+					RGFW_focusCallback(win, 1);
+				}
+				else {
+					win->event.type = RGFW_focusOut;
+					RGFW_focusCallback(win, 0);
+				}
+
+				break;
+			
+			case WM_PAINT:
+				win->event.type = RGFW_windowRefresh;
+				RGFW_windowRefreshCallback(win);
+				break;
+			
+			case WM_MOUSELEAVE:
+				win->event.type = RGFW_mouseLeave;
+				win->_winArgs |= RGFW_MOUSE_LEFT;
+				RGFW_mouseNotifyCallBack(win, win->event.point, 0);
+				break;
+			
+			case WM_KEYUP: {
+				win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam);
+								
+				RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode);
+
+				static char keyName[16];
+				
+				{
+					GetKeyNameTextA((LONG) msg.lParam, keyName, 16);
+
+					if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) ||
+						((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) {
+						CharLowerBuffA(keyName, 16);
+					}
+				}
+
+				RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001));
+
+				strncpy(win->event.keyName, keyName, 16);
+
+				if (RGFW_isPressed(win, RGFW_ShiftL)) {
+					ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR),
+						keyboardState, (LPWORD) win->event.keyName, 0);
+				}
+
+				win->event.type = RGFW_keyReleased;
+				RGFW_keyboard[win->event.keyCode].current = 0;
+				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0);
+				break;
+			}
+			case WM_KEYDOWN: {
+				win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam);
+
+				RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode);
+
+				static char keyName[16];
+				
+				{
+					GetKeyNameTextA((LONG) msg.lParam, keyName, 16);
+
+					if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) ||
+						((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) {
+						CharLowerBuffA(keyName, 16);
+					}
+				}
+				
+				RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001));
+
+				strncpy(win->event.keyName, keyName, 16);
+
+				if (RGFW_isPressed(win, RGFW_ShiftL) & 0x8000) {
+					ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR),
+						keyboardState, (LPWORD) win->event.keyName, 0);
+				}
+
+				win->event.type = RGFW_keyPressed;
+				win->event.repeat = RGFW_isPressed(win, win->event.keyCode);
+				RGFW_keyboard[win->event.keyCode].current = 1;
+				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1);
+				break;
+			}
+
+			case WM_MOUSEMOVE:
+				if ((win->_winArgs & RGFW_HOLD_MOUSE))
+					break;
+
+				win->event.type = RGFW_mousePosChanged;
+
+				win->event.point.x = GET_X_LPARAM(msg.lParam);
+				win->event.point.y = GET_Y_LPARAM(msg.lParam);
+				
+				RGFW_mousePosCallback(win, win->event.point);
+
+				if (win->_winArgs & RGFW_MOUSE_LEFT) {
+					win->_winArgs ^= RGFW_MOUSE_LEFT;
+					win->event.type = RGFW_mouseEnter;
+					RGFW_mouseNotifyCallBack(win, win->event.point, 1);
+				}
+
+				break;
+
+			case WM_INPUT: {
+				if (!(win->_winArgs & RGFW_HOLD_MOUSE))
+					break;
+				
+				unsigned size = sizeof(RAWINPUT);
+				static RAWINPUT raw[sizeof(RAWINPUT)];
+				GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, raw, &size, sizeof(RAWINPUTHEADER));
+
+				if (raw->header.dwType != RIM_TYPEMOUSE || (raw->data.mouse.lLastX == 0 && raw->data.mouse.lLastY == 0) )
+					break;
+				
+				win->event.type = RGFW_mousePosChanged;
+				win->event.point.x = raw->data.mouse.lLastX;
+				win->event.point.y = raw->data.mouse.lLastY;
+				break;
+			}
+
+			case WM_LBUTTONDOWN:
+				win->event.button = RGFW_mouseLeft;
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 1;
+				win->event.type = RGFW_mouseButtonPressed;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+				break;
+			case WM_RBUTTONDOWN:
+				win->event.button = RGFW_mouseRight;
+				win->event.type = RGFW_mouseButtonPressed;
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 1;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+				break;
+			case WM_MBUTTONDOWN:
+				win->event.button = RGFW_mouseMiddle;
+				win->event.type = RGFW_mouseButtonPressed;
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 1;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+				break;
+
+			case WM_MOUSEWHEEL:
+				if (msg.wParam > 0)
+					win->event.button = RGFW_mouseScrollUp;
+				else
+					win->event.button = RGFW_mouseScrollDown;
+
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 1;
+
+				win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA;
+
+				win->event.type = RGFW_mouseButtonPressed;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+				break;
+
+			case WM_LBUTTONUP:
+			
+				win->event.button = RGFW_mouseLeft;
+				win->event.type = RGFW_mouseButtonReleased;
+
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 0;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
+				break;
+			case WM_RBUTTONUP:
+				win->event.button = RGFW_mouseRight;
+				win->event.type = RGFW_mouseButtonReleased;
+
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 0;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
+				break;
+			case WM_MBUTTONUP:
+				win->event.button = RGFW_mouseMiddle;
+				win->event.type = RGFW_mouseButtonReleased;
+
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 0;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
+				break;
+
+				/*
+					much of this event is source from glfw
+				*/
+			case WM_DROPFILES: {				
+				win->event.type = RGFW_dnd_init;
+
+				drop = (HDROP) msg.wParam;
+				POINT pt;
+
+				/* Move the mouse to the position of the drop */
+				DragQueryPoint(drop, &pt);
+
+				win->event.point.x = pt.x;
+				win->event.point.y = pt.y;
+
+				RGFW_dndInitCallback(win, win->event.point);
+			}
+				break;
+			case WM_GETMINMAXINFO:
+			{
+				if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0)
+					break;
+
+				MINMAXINFO* mmi = (MINMAXINFO*) msg.lParam;
+				mmi->ptMinTrackSize.x = win->src.minSize.w;
+				mmi->ptMinTrackSize.y = win->src.minSize.h;
+				mmi->ptMaxTrackSize.x = win->src.maxSize.w;
+				mmi->ptMaxTrackSize.y = win->src.maxSize.h;
+				return 0;
+			}
+			default:
+				win->event.type = 0;
+				break;
+			}
+
+			TranslateMessage(&msg);
+			DispatchMessageA(&msg);
+		}
+
+		else
+			win->event.type = 0;
+
+		if (!IsWindow(win->src.window)) {
+			win->event.type = RGFW_quit;
+			RGFW_windowQuitCallback(win);
+		}
+
+		if (win->event.type)
+			return &win->event;
+		else
+			return NULL;
+	}
+
+	u8 RGFW_window_isFullscreen(RGFW_window* win) {
+		assert(win != NULL);
+
+		#ifndef __cplusplus
+		WINDOWPLACEMENT placement = { 0 };
+		#else
+		WINDOWPLACEMENT placement = {  };
+		#endif
+		GetWindowPlacement(win->src.window, &placement);
+		return placement.showCmd == SW_SHOWMAXIMIZED;
+	}
+
+	u8 RGFW_window_isHidden(RGFW_window* win) {
+		assert(win != NULL);
+
+		return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win);
+	}
+
+	u8 RGFW_window_isMinimized(RGFW_window* win) {
+		assert(win != NULL);
+
+		#ifndef __cplusplus
+		WINDOWPLACEMENT placement = { 0 };
+		#else
+		WINDOWPLACEMENT placement = {  };
+		#endif
+		GetWindowPlacement(win->src.window, &placement);
+		return placement.showCmd == SW_SHOWMINIMIZED;
+	}
+
+	u8 RGFW_window_isMaximized(RGFW_window* win) {
+		assert(win != NULL);
+
+		#ifndef __cplusplus
+		WINDOWPLACEMENT placement = { 0 };
+		#else
+		WINDOWPLACEMENT placement = {  };
+		#endif
+		GetWindowPlacement(win->src.window, &placement);
+		return placement.showCmd == SW_SHOWMAXIMIZED;
+	}
+
+	typedef struct { int iIndex; HMONITOR hMonitor; } RGFW_mInfo;
+	BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+		RGFW_UNUSED(hdcMonitor)
+		RGFW_UNUSED(lprcMonitor)
+
+		RGFW_mInfo* info = (RGFW_mInfo*) dwData;
+		if (info->hMonitor == hMonitor)
+			return FALSE;
+
+		info->iIndex++;
+		return TRUE;
+	}
+	
+	#ifndef RGFW_NO_MONITOR
+	RGFW_monitor win32CreateMonitor(HMONITOR src) {
+		RGFW_monitor monitor;
+		MONITORINFO monitorInfo;
+
+		monitorInfo.cbSize = sizeof(MONITORINFO);
+		GetMonitorInfoA(src, &monitorInfo);
+
+		RGFW_mInfo info;
+		info.iIndex = 0;
+		info.hMonitor = src;
+
+		/* get the monitor's index */
+		if (EnumDisplayMonitors(NULL, NULL, GetMonitorByHandle, (LPARAM) &info)) {
+			DISPLAY_DEVICEA dd;
+			dd.cb = sizeof(dd);
+
+			/* loop through the devices until you find a device with the monitor's index */
+			size_t deviceIndex;
+			for (deviceIndex = 0; EnumDisplayDevicesA(0, (DWORD) deviceIndex, &dd, 0); deviceIndex++) {
+				char* deviceName = dd.DeviceName;
+				if (EnumDisplayDevicesA(deviceName, info.iIndex, &dd, 0)) {
+					strncpy(monitor.name, dd.DeviceString, 128); /*!< copy the monitor's name */
+					break;
+				}
+			}
+		}
+
+		monitor.rect.x = monitorInfo.rcWork.left;
+		monitor.rect.y = monitorInfo.rcWork.top;
+		monitor.rect.w = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
+		monitor.rect.h = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
+
+#ifndef RGFW_NO_DPI
+		#ifndef USER_DEFAULT_SCREEN_DPI
+		#define USER_DEFAULT_SCREEN_DPI 96
+		#endif
+
+		if (GetDpiForMonitor != NULL) {
+			u32 x, y;
+			GetDpiForMonitor(src, MDT_EFFECTIVE_DPI, &x, &y);
+			
+			monitor.scaleX = (float) (x) / (float) USER_DEFAULT_SCREEN_DPI;
+			monitor.scaleY = (float) (y) / (float) USER_DEFAULT_SCREEN_DPI;
+		}
+#endif
+
+		HDC hdc = GetDC(NULL);
+		/* get pixels per inch */
+		i32 ppiX = GetDeviceCaps(hdc, LOGPIXELSX);
+		i32 ppiY = GetDeviceCaps(hdc, LOGPIXELSY);
+		ReleaseDC(NULL, hdc);
+
+		/* Calculate physical height in inches */
+		monitor.physW = GetSystemMetrics(SM_CYSCREEN) / (float) ppiX;
+		monitor.physH = GetSystemMetrics(SM_CXSCREEN) / (float) ppiY;
+		
+		return monitor;
+	}
+	#endif /* RGFW_NO_MONITOR */
+	
+
+	#ifndef RGFW_NO_MONITOR
+	RGFW_monitor RGFW_monitors[6];
+	BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+		RGFW_UNUSED(hdcMonitor)
+		RGFW_UNUSED(lprcMonitor)
+
+		RGFW_mInfo* info = (RGFW_mInfo*) dwData;
+
+		if (info->iIndex >= 6)
+			return FALSE;
+
+		RGFW_monitors[info->iIndex] = win32CreateMonitor(hMonitor);
+		info->iIndex++;
+
+		return TRUE;
+	}
+
+	RGFW_monitor RGFW_getPrimaryMonitor(void) {
+        #ifdef __cplusplus
+        return win32CreateMonitor(MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
+        #else
+		return win32CreateMonitor(MonitorFromPoint((POINT) { 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
+	    #endif
+    }
+
+	RGFW_monitor* RGFW_getMonitors(void) {
+		RGFW_mInfo info;
+		info.iIndex = 0;
+		while (EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info));
+			
+		return RGFW_monitors;
+	}
+
+	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
+		HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY);
+		return win32CreateMonitor(src);
+	}
+	#endif
+
+	HICON RGFW_loadHandleImage(RGFW_window* win, u8* src, RGFW_area a, BOOL icon) {
+		assert(win != NULL);
+
+		u32 i;
+		HDC dc;
+		HICON handle;
+		HBITMAP color, mask;
+		BITMAPV5HEADER bi;
+		ICONINFO ii;
+		u8* target = NULL;
+		u8* source = src;
+
+		ZeroMemory(&bi, sizeof(bi));
+		bi.bV5Size = sizeof(bi);
+		bi.bV5Width = a.w;
+		bi.bV5Height = -((LONG) a.h);
+		bi.bV5Planes = 1;
+		bi.bV5BitCount = 32;
+		bi.bV5Compression = BI_BITFIELDS;
+		bi.bV5RedMask = 0x00ff0000;
+		bi.bV5GreenMask = 0x0000ff00;
+		bi.bV5BlueMask = 0x000000ff;
+		bi.bV5AlphaMask = 0xff000000;
+
+		dc = GetDC(NULL);
+		color = CreateDIBSection(dc,
+			(BITMAPINFO*) &bi,
+			DIB_RGB_COLORS,
+			(void**) &target,
+			NULL,
+			(DWORD) 0);
+		ReleaseDC(NULL, dc);
+
+		mask = CreateBitmap(a.w, a.h, 1, 1, NULL);
+
+		for (i = 0; i < a.w * a.h; i++) {
+			target[0] = source[2];
+			target[1] = source[1];
+			target[2] = source[0];
+			target[3] = source[3];
+			target += 4;
+			source += 4;
+		}
+
+		ZeroMemory(&ii, sizeof(ii));
+		ii.fIcon = icon;
+		ii.xHotspot = 0;
+		ii.yHotspot = 0;
+		ii.hbmMask = mask;
+		ii.hbmColor = color;
+
+		handle = CreateIconIndirect(&ii);
+
+		DeleteObject(color);
+		DeleteObject(mask);
+
+		return handle;
+	}
+
+	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
+		assert(win != NULL);
+		RGFW_UNUSED(channels)
+
+		HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(win, image, a, FALSE);
+		SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) cursor);
+		SetCursor(cursor);
+		DestroyCursor(cursor);
+	}
+
+	void RGFW_window_setMouseDefault(RGFW_window* win) {
+		RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW);
+	}
+
+	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
+		assert(win != NULL);
+
+		if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u32)))
+			return;
+
+		char* icon = MAKEINTRESOURCEA(RGFW_mouseIconSrc[mouse]);
+
+		SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon));
+		SetCursor(LoadCursorA(NULL, icon));
+	}
+
+	void RGFW_window_hide(RGFW_window* win) {
+		ShowWindow(win->src.window, SW_HIDE);
+	}
+
+	void RGFW_window_show(RGFW_window* win) {
+		ShowWindow(win->src.window, SW_RESTORE);
+	}
+
+	void RGFW_window_close(RGFW_window* win) {
+		assert(win != NULL);
+
+#ifdef RGFW_EGL
+		RGFW_closeEGL(win);
+#endif
+
+		if (win == RGFW_root) {
+#ifdef RGFW_DIRECTX
+			RGFW_dxInfo.pDeviceContext->lpVtbl->Release(RGFW_dxInfo.pDeviceContext);
+			RGFW_dxInfo.pDevice->lpVtbl->Release(RGFW_dxInfo.pDevice);
+			RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter);
+			RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory);
+#endif
+		
+			if (RGFW_XInput_dll != NULL) {
+				FreeLibrary(RGFW_XInput_dll);
+				RGFW_XInput_dll = NULL;
+			}
+
+			#ifndef RGFW_NO_DPI
+			if (RGFW_Shcore_dll != NULL) {
+				FreeLibrary(RGFW_Shcore_dll);
+				RGFW_Shcore_dll = NULL;
+			}
+			#endif
+
+			if (wglinstance != NULL) {
+				FreeLibrary(wglinstance);
+				wglinstance = NULL;
+			}
+
+			RGFW_root = NULL;
+		}
+
+#ifdef RGFW_DIRECTX
+		win->src.swapchain->lpVtbl->Release(win->src.swapchain);
+		win->src.renderTargetView->lpVtbl->Release(win->src.renderTargetView);
+		win->src.pDepthStencilView->lpVtbl->Release(win->src.pDepthStencilView);
+#endif
+
+#ifdef RGFW_BUFFER
+		DeleteDC(win->src.hdcMem);
+		DeleteObject(win->src.bitmap);
+#endif
+
+#ifdef RGFW_OPENGL
+		wglDeleteContext((HGLRC) win->src.ctx); /*!< delete opengl context */
+#endif
+		DeleteDC(win->src.hdc); /*!< delete device context */
+		DestroyWindow(win->src.window); /*!< delete window */
+
+#if defined(RGFW_OSMESA)
+		if (win->buffer != NULL)
+			RGFW_FREE(win->buffer);
+#endif
+
+#ifdef RGFW_ALLOC_DROPFILES
+		{
+			u32 i;
+			for (i = 0; i < RGFW_MAX_DROPS; i++)
+				RGFW_FREE(win->event.droppedFiles[i]);
+
+
+			RGFW_FREE(win->event.droppedFiles);
+		}
+#endif
+
+		RGFW_FREE(win);
+	}
+
+	void RGFW_window_move(RGFW_window* win, RGFW_point v) {
+		assert(win != NULL);
+
+		win->r.x = v.x;
+		win->r.y = v.y;
+		SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, 0, 0, SWP_NOSIZE);
+	}
+
+	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
+		assert(win != NULL);
+
+		win->r.w = a.w;
+		win->r.h = a.h;
+		SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w, win->r.h + win->src.hOffset, SWP_NOMOVE);
+	}
+
+
+	void RGFW_window_setName(RGFW_window* win, char* name) {
+		assert(win != NULL);
+
+		SetWindowTextA(win->src.window, name);
+	}
+
+	/* sourced from GLFW */
+	#ifndef RGFW_NO_PASSTHROUGH
+	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
+		assert(win != NULL);
+		
+		COLORREF key = 0;
+		BYTE alpha = 0;
+		DWORD flags = 0;
+		DWORD exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE);
+		
+		if (exStyle & WS_EX_LAYERED)
+			GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags);
+
+		if (passthrough)
+			exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
+		else
+		{
+			exStyle &= ~WS_EX_TRANSPARENT;
+			// NOTE: Window opacity also needs the layered window style so do not
+			//       remove it if the window is alpha blended
+			if (exStyle & WS_EX_LAYERED)
+			{
+				if (!(flags & LWA_ALPHA))
+					exStyle &= ~WS_EX_LAYERED;
+			}
+		}
+
+		SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle);
+
+		if (passthrough) {
+			SetLayeredWindowAttributes(win->src.window, key, alpha, flags);
+		}
+	}
+	#endif
+
+	/* much of this function is sourced from GLFW */
+	void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) {
+		assert(win != NULL);
+		#ifndef RGFW_WIN95
+		RGFW_UNUSED(channels)
+
+		HICON handle = RGFW_loadHandleImage(win, src, a, TRUE);
+
+		SetClassLongPtrA(win->src.window, GCLP_HICON, (LPARAM) handle);
+
+		DestroyIcon(handle);
+		#else
+		RGFW_UNUSED(src)
+		RGFW_UNUSED(a)
+		RGFW_UNUSED(channels)
+		#endif
+	}
+
+	char* RGFW_readClipboard(size_t* size) {
+		/* Open the clipboard */
+		if (OpenClipboard(NULL) == 0)
+			return (char*) "";
+
+		/* Get the clipboard data as a Unicode string */
+		HANDLE hData = GetClipboardData(CF_UNICODETEXT);
+		if (hData == NULL) {
+			CloseClipboard();
+			return (char*) "";
+		}
+
+		wchar_t* wstr = (wchar_t*) GlobalLock(hData);
+
+		char* text;
+
+		{
+			setlocale(LC_ALL, "en_US.UTF-8");
+
+			size_t textLen = wcstombs(NULL, wstr, 0);
+			if (textLen == 0)
+				return (char*) "";
+
+			text = (char*) RGFW_MALLOC((textLen * sizeof(char)) + 1);
+
+			wcstombs(text, wstr, (textLen) +1);
+
+			if (size != NULL)
+				*size = textLen + 1;
+
+			text[textLen] = '\0';
+		}
+
+		/* Release the clipboard data */
+		GlobalUnlock(hData);
+		CloseClipboard();
+
+		return text;
+	}
+
+	void RGFW_writeClipboard(const char* text, u32 textLen) {
+		HANDLE object;
+		WCHAR* buffer;
+
+		object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR));
+		if (!object)
+			return;
+
+		buffer = (WCHAR*) GlobalLock(object);
+		if (!buffer) {
+			GlobalFree(object);
+			return;
+		}
+
+		MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, textLen);
+		GlobalUnlock(object);
+
+		if (!OpenClipboard(RGFW_root->src.window)) {
+			GlobalFree(object);
+			return;
+		}
+
+		EmptyClipboard();
+		SetClipboardData(CF_UNICODETEXT, object);
+		CloseClipboard();
+	}
+
+	u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) {
+		assert(win != NULL);
+
+		RGFW_UNUSED(jsNumber)
+
+		return RGFW_registerJoystickF(win, (char*) "");
+	}
+
+	u16 RGFW_registerJoystickF(RGFW_window* win, char* file) {
+		assert(win != NULL);
+		RGFW_UNUSED(file)
+
+		return RGFW_joystickCount - 1;
+	}
+
+	void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) {
+		assert(win != NULL);
+
+		SetCursorPos(p.x, p.y);
+	}
+
+	#ifdef RGFW_OPENGL
+	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+		if (win == NULL)
+			wglMakeCurrent(NULL, NULL);
+		else
+			wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx);
+	}
+	#endif
+
+	#ifndef RGFW_EGL
+	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
+		assert(win != NULL);
+		
+		#if defined(RGFW_OPENGL)
+		typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);
+		static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
+		static void* loadSwapFunc = (void*) 1;
+
+		if (loadSwapFunc == NULL) {
+			fprintf(stderr, "wglSwapIntervalEXT not supported\n");
+			return;
+		}
+
+		if (wglSwapIntervalEXT == NULL) {
+			loadSwapFunc = (void*) wglGetProcAddress("wglSwapIntervalEXT");
+			wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) loadSwapFunc;
+		}
+
+		if (wglSwapIntervalEXT(swapInterval) == FALSE)
+			fprintf(stderr, "Failed to set swap interval\n");
+		#else
+        RGFW_UNUSED(swapInterval);
+        #endif
+
+	}
+	#endif
+
+	void RGFW_window_swapBuffers(RGFW_window* win) {
+		//assert(win != NULL);
+		/* clear the window*/
+
+		if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) {
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+			#ifdef RGFW_OSMESA
+			RGFW_OSMesa_reorganize();
+			#endif
+
+			HGDIOBJ oldbmp = SelectObject(win->src.hdcMem, win->src.bitmap);
+			BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY);
+			SelectObject(win->src.hdcMem, oldbmp);
+#endif
+		}
+
+		if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) {
+			#ifdef RGFW_EGL
+					eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
+			#elif defined(RGFW_OPENGL)
+					SwapBuffers(win->src.hdc);
+			#endif
+
+			#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX)
+					win->src.swapchain->lpVtbl->Present(win->src.swapchain, 0, 0);
+			#endif
+		}
+	}
+
+	char* createUTF8FromWideStringWin32(const WCHAR* source) {
+		char* target;
+		i32 size;
+
+		size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL);
+		if (!size) {
+			return NULL;
+		}
+
+		target = (char*) RGFW_CALLOC(size, 1);
+
+		if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) {
+			RGFW_FREE(target);
+			return NULL;
+		}
+
+		return target;
+	}
+	
+    static inline LARGE_INTEGER RGFW_win32_initTimer(void) {
+		static LARGE_INTEGER frequency = {{0, 0}};
+		if (frequency.QuadPart == 0) {
+			timeBeginPeriod(1);
+			QueryPerformanceFrequency(&frequency);
+		}
+
+		return frequency;
+	}
+
+	u64 RGFW_getTimeNS(void) {
+		LARGE_INTEGER frequency = RGFW_win32_initTimer();
+
+		LARGE_INTEGER counter;
+		QueryPerformanceCounter(&counter);
+
+		return (u64) ((counter.QuadPart * 1e9) / frequency.QuadPart);
+	}
+
+	u64 RGFW_getTime(void) {
+		LARGE_INTEGER frequency = RGFW_win32_initTimer();
+
+		LARGE_INTEGER counter;
+		QueryPerformanceCounter(&counter);
+		return (u64) (counter.QuadPart / (double) frequency.QuadPart);
+	}
+	
+	void RGFW_sleep(u64 ms) {
+		Sleep(ms);
+	}
+
+#ifndef RGFW_NO_THREADS
+	RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { return CreateThread(NULL, 0, ptr, args, 0, NULL); }
+	void RGFW_cancelThread(RGFW_thread thread) { CloseHandle((HANDLE) thread); }
+	void RGFW_joinThread(RGFW_thread thread) { WaitForSingleObject((HANDLE) thread, INFINITE); }
+	void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { SetThreadPriority((HANDLE) thread, priority); }
+#endif
+#endif /* RGFW_WINDOWS */
+
+/*
+	End of Windows defines
+*/
+
+
+
+/* 
+
+	Start of MacOS defines
+
+
+*/
+
+#if defined(RGFW_MACOS)
+	/*
+		based on silicon.h
+		start of cocoa wrapper
+	*/
+
+#include <CoreVideo/CVDisplayLink.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
+#include <mach/mach_time.h>
+
+	typedef CGRect NSRect;
+	typedef CGPoint NSPoint;
+	typedef CGSize NSSize;
+
+	typedef void NSBitmapImageRep;
+	typedef void NSCursor;
+	typedef void NSDraggingInfo;
+	typedef void NSWindow;
+	typedef void NSApplication;
+	typedef void NSScreen;
+	typedef void NSEvent;
+	typedef void NSString;
+	typedef void NSOpenGLContext;
+	typedef void NSPasteboard;
+	typedef void NSColor;
+	typedef void NSArray;
+	typedef void NSImageRep;
+	typedef void NSImage;
+	typedef void NSOpenGLView;
+
+
+	typedef const char* NSPasteboardType;
+	typedef unsigned long NSUInteger;
+	typedef long NSInteger;
+	typedef NSInteger NSModalResponse;
+
+#ifdef __arm64__
+	/* ARM just uses objc_msgSend */
+#define abi_objc_msgSend_stret objc_msgSend
+#define abi_objc_msgSend_fpret objc_msgSend
+#else /* __i386__ */ 
+	/* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */
+#define abi_objc_msgSend_stret objc_msgSend_stret
+#define abi_objc_msgSend_fpret objc_msgSend_fpret
+#endif
+
+#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc"))
+#define objc_msgSend_bool			((BOOL (*)(id, SEL))objc_msgSend)
+#define objc_msgSend_void			((void (*)(id, SEL))objc_msgSend)
+#define objc_msgSend_void_id		((void (*)(id, SEL, id))objc_msgSend)
+#define objc_msgSend_uint			((NSUInteger (*)(id, SEL))objc_msgSend)
+#define objc_msgSend_void_bool		((void (*)(id, SEL, BOOL))objc_msgSend)
+#define objc_msgSend_bool_void		((BOOL (*)(id, SEL))objc_msgSend)
+#define objc_msgSend_void_SEL		((void (*)(id, SEL, SEL))objc_msgSend)
+#define objc_msgSend_id				((id (*)(id, SEL))objc_msgSend)
+#define objc_msgSend_id_id				((id (*)(id, SEL, id))objc_msgSend)
+#define objc_msgSend_id_bool			((BOOL (*)(id, SEL, id))objc_msgSend)
+#define objc_msgSend_int ((id (*)(id, SEL, int))objc_msgSend)
+#define objc_msgSend_arr ((id (*)(id, SEL, int))objc_msgSend)
+#define objc_msgSend_ptr ((id (*)(id, SEL, void*))objc_msgSend)
+#define objc_msgSend_class ((id (*)(Class, SEL))objc_msgSend)
+#define objc_msgSend_class_char ((id (*)(Class, SEL, char*))objc_msgSend)
+
+	NSApplication* NSApp = NULL;
+
+	void NSRelease(id obj) {
+		objc_msgSend_void(obj, sel_registerName("release"));
+	}
+
+	#define release NSRelease
+
+	NSString* NSString_stringWithUTF8String(const char* str) {	
+		return ((id(*)(id, SEL, const char*))objc_msgSend)
+			((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str);
+	}
+
+	const char* NSString_to_char(NSString* str) {
+		return ((const char* (*)(id, SEL)) objc_msgSend) (str, sel_registerName("UTF8String"));
+	}
+
+	void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) {
+		Class selected_class;
+
+		if (strcmp(class_name, "NSView") == 0) {
+			selected_class = objc_getClass("ViewClass");
+		} else if (strcmp(class_name, "NSWindow") == 0) {
+			selected_class = objc_getClass("WindowClass");
+		} else {
+			selected_class = objc_getClass(class_name);
+		}
+
+		class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0);
+	}
+
+	/* Header for the array. */
+	typedef struct siArrayHeader {
+		size_t count;
+		/* TODO(EimaMei): Add a `type_width` later on. */
+	} siArrayHeader;
+
+	/* Gets the header of the siArray. */
+#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1)
+
+	void* si_array_init_reserve(size_t sizeof_element, size_t count) {
+		siArrayHeader* ptr = malloc(sizeof(siArrayHeader) + (sizeof_element * count));
+		void* array = ptr + sizeof(siArrayHeader);
+
+		siArrayHeader* header = SI_ARRAY_HEADER(array);
+		header->count = count;
+
+		return array;
+	}
+
+#define si_array_len(array) (SI_ARRAY_HEADER(array)->count)
+#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", function)
+	/* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/
+#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", function)
+	
+	unsigned char* NSBitmapImageRep_bitmapData(NSBitmapImageRep* imageRep) {
+		return ((unsigned char* (*)(id, SEL))objc_msgSend)
+			(imageRep, sel_registerName("bitmapData"));
+	}
+
+#define NS_ENUM(type, name) type name; enum
+
+	typedef NS_ENUM(NSUInteger, NSBitmapFormat) {
+		NSBitmapFormatAlphaFirst = 1 << 0,       // 0 means is alpha last (RGBA, CMYKA, etc.)
+			NSBitmapFormatAlphaNonpremultiplied = 1 << 1,       // 0 means is premultiplied
+			NSBitmapFormatFloatingPointSamples = 1 << 2,  // 0 is integer
+
+			NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8),
+			NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9),
+			NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10),
+			NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11)
+	};
+
+	NSBitmapImageRep* NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) {
+		void* func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:");
+
+		return (NSBitmapImageRep*) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, const char*, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend)
+			(NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits);
+	}
+
+	NSColor* NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) {
+		void* nsclass = objc_getClass("NSColor");
+		void* func = sel_registerName("colorWithSRGBRed:green:blue:alpha:");
+		return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)
+			(nsclass, func, red, green, blue, alpha);
+	}
+
+	NSCursor* NSCursor_initWithImage(NSImage* newImage, NSPoint aPoint) {
+		void* func = sel_registerName("initWithImage:hotSpot:");
+		void* nsclass = objc_getClass("NSCursor");
+
+		return (NSCursor*) ((id(*)(id, SEL, id, NSPoint))objc_msgSend)
+			(NSAlloc(nsclass), func, newImage, aPoint);
+	}
+
+	void NSImage_addRepresentation(NSImage* image, NSImageRep* imageRep) {
+		void* func = sel_registerName("addRepresentation:");
+		objc_msgSend_void_id(image, func, imageRep);
+	}
+
+	NSImage* NSImage_initWithSize(NSSize size) {
+		void* func = sel_registerName("initWithSize:");
+		return ((id(*)(id, SEL, NSSize))objc_msgSend)
+			(NSAlloc((id)objc_getClass("NSImage")), func, size);
+	}
+#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers))
+	typedef NS_ENUM(NSInteger, NSOpenGLContextParameter) {
+		NSOpenGLContextParameterSwapInterval           NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param.  0 -> Don't sync, 1 -> Sync to vertical retrace     */
+			NSOpenGLContextParametectxaceOrder           NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param.  1 -> Above Window (default), -1 -> Below Window    */
+			NSOpenGLContextParametectxaceOpacity         NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param.  1-> Surface is opaque (default), 0 -> non-opaque   */
+			NSOpenGLContextParametectxaceBackingSize     NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params.  Width/height of surface backing size              */
+			NSOpenGLContextParameterReclaimResources       NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params.                                                    */
+			NSOpenGLContextParameterCurrentRendererID      NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param.   Retrieves the current renderer ID                 */
+			NSOpenGLContextParameterGPUVertexProcessing    NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param.   Currently processing vertices with GPU (get)      */
+			NSOpenGLContextParameterGPUFragmentProcessing  NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param.   Currently processing fragments with GPU (get)     */
+			NSOpenGLContextParameterHasDrawable            NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param.   Boolean returned if drawable is attached          */
+			NSOpenGLContextParameterMPSwapsInFlight        NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param.   Max number of swaps queued by the MP GL engine    */
+
+			NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params.  Set or get the swap rectangle {x, y, w, h} */
+			NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */
+			NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */
+			NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */
+			NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param.   Surface volatile state */
+	};
+
+
+	void NSOpenGLContext_setValues(NSOpenGLContext* context, const int* vals, NSOpenGLContextParameter param) {
+		void* func = sel_registerName("setValues:forParameter:");
+		((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend)
+			(context, func, vals, param);
+	}
+
+	void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) {
+		void* func = sel_registerName("initWithAttributes:");
+		return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend)
+			(NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), func, attribs);
+	}
+
+	NSOpenGLView* NSOpenGLView_initWithFrame(NSRect frameRect, uint32_t* format) {
+		void* func = sel_registerName("initWithFrame:pixelFormat:");
+		return (NSOpenGLView*) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend)
+			(NSAlloc((id)objc_getClass("NSOpenGLView")), func, frameRect, format);
+	}
+
+	void NSCursor_performSelector(NSCursor* cursor, void* selector) {
+		void* func = sel_registerName("performSelector:");
+		objc_msgSend_void_SEL(cursor, func, selector);
+	}
+
+	NSPasteboard* NSPasteboard_generalPasteboard(void) {
+		return (NSPasteboard*) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard"));
+	}
+
+	NSString** cstrToNSStringArray(char** strs, size_t len) {
+		static NSString* nstrs[6];
+		size_t i;
+		for (i = 0; i < len; i++)
+			nstrs[i] = NSString_stringWithUTF8String(strs[i]);
+
+		return nstrs;
+	}
+
+	const char* NSPasteboard_stringForType(NSPasteboard* pasteboard, NSPasteboardType dataType) {
+		void* func = sel_registerName("stringForType:");
+		return (const char*) NSString_to_char(((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, func, NSString_stringWithUTF8String(dataType)));
+	}
+
+	NSArray* c_array_to_NSArray(void* array, size_t len) {
+		SEL func = sel_registerName("initWithObjects:count:");
+		void* nsclass = objc_getClass("NSArray");
+		return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend)
+					(NSAlloc(nsclass), func, array, len);
+	}
+ 
+	void NSregisterForDraggedTypes(void* view, NSPasteboardType* newTypes, size_t len) {
+		NSString** ntypes = cstrToNSStringArray((char**)newTypes, len);
+
+		NSArray* array = c_array_to_NSArray(ntypes, len);
+		objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array);
+		NSRelease(array);
+	}
+
+	NSInteger NSPasteBoard_declareTypes(NSPasteboard* pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) {
+		NSString** ntypes = cstrToNSStringArray((char**)newTypes, len);
+
+		void* func = sel_registerName("declareTypes:owner:");
+
+		NSArray* array = c_array_to_NSArray(ntypes, len);
+
+		NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend)
+			(pasteboard, func, array, owner);
+		NSRelease(array);
+
+		return output;
+	}
+
+	bool NSPasteBoard_setString(NSPasteboard* pasteboard, const char* stringToWrite, NSPasteboardType dataType) {
+		void* func = sel_registerName("setString:forType:");
+		return ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend)
+			(pasteboard, func, NSString_stringWithUTF8String(stringToWrite), NSString_stringWithUTF8String(dataType));
+	}
+
+	void NSRetain(id obj) { objc_msgSend_void(obj, sel_registerName("retain")); }
+
+	typedef enum NSApplicationActivationPolicy {
+		NSApplicationActivationPolicyRegular,
+		NSApplicationActivationPolicyAccessory,
+		NSApplicationActivationPolicyProhibited
+	} NSApplicationActivationPolicy;
+
+	typedef NS_ENUM(u32, NSBackingStoreType) {
+		NSBackingStoreRetained = 0,
+			NSBackingStoreNonretained = 1,
+			NSBackingStoreBuffered = 2
+	};
+
+	typedef NS_ENUM(u32, NSWindowStyleMask) {
+		NSWindowStyleMaskBorderless = 0,
+			NSWindowStyleMaskTitled = 1 << 0,
+			NSWindowStyleMaskClosable = 1 << 1,
+			NSWindowStyleMaskMiniaturizable = 1 << 2,
+			NSWindowStyleMaskResizable = 1 << 3,
+			NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */
+			NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12,
+			NSWindowStyleMaskFullScreen = 1 << 14,
+			NSWindowStyleMaskFullSizeContentView = 1 << 15,
+			NSWindowStyleMaskUtilityWindow = 1 << 4,
+			NSWindowStyleMaskDocModalWindow = 1 << 6,
+			NSWindowStyleMaskNonactivatingPanel = 1 << 7,
+			NSWindowStyleMaskHUDWindow = 1 << 13
+	};
+
+	NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPboardType
+
+
+	typedef NS_ENUM(i32, NSDragOperation) {
+		NSDragOperationNone = 0,
+			NSDragOperationCopy = 1,
+			NSDragOperationLink = 2,
+			NSDragOperationGeneric = 4,
+			NSDragOperationPrivate = 8,
+			NSDragOperationMove = 16,
+			NSDragOperationDelete = 32,
+			NSDragOperationEvery = ULONG_MAX,
+
+			//NSDragOperationAll_Obsolete	API_DEPRECATED("", macos(10.0,10.10)) = 15, // Use NSDragOperationEvery
+			//NSDragOperationAll API_DEPRECATED("", macos(10.0,10.10)) = NSDragOperationAll_Obsolete, // Use NSDragOperationEvery
+	};
+
+	void* NSArray_objectAtIndex(NSArray* array, NSUInteger index) {
+		void* func = sel_registerName("objectAtIndex:");
+		return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index);
+	}
+
+	const char** NSPasteboard_readObjectsForClasses(NSPasteboard* pasteboard, Class* classArray, size_t len, void* options) {
+		void* func = sel_registerName("readObjectsForClasses:options:");
+
+		NSArray* array = c_array_to_NSArray(classArray, len);
+
+		NSArray* output = (NSArray*) ((id(*)(id, SEL, id, void*))objc_msgSend)
+			(pasteboard, func, array, options);
+
+		NSRelease(array);
+		NSUInteger count = ((NSUInteger(*)(id, SEL))objc_msgSend)(output, sel_registerName("count"));
+
+		const char** res = si_array_init_reserve(sizeof(const char*), count);
+
+		void* path_func = sel_registerName("path");
+
+		for (NSUInteger i = 0; i < count; i++) {
+			void* url = NSArray_objectAtIndex(output, i);
+			NSString* url_str = ((id(*)(id, SEL))objc_msgSend)(url, path_func);
+			res[i] = NSString_to_char(url_str);
+		}
+
+		return res;
+	}
+
+	void* NSWindow_contentView(NSWindow* window) {
+		void* func = sel_registerName("contentView");
+		return objc_msgSend_id(window, func);
+	}
+
+	/*
+		End of cocoa wrapper
+	*/
+
+	char* RGFW_mouseIconSrc[] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"};
+
+	void* RGFWnsglFramework = NULL;
+
+#ifdef RGFW_OPENGL
+	void* RGFW_getProcAddress(const char* procname) {
+		if (RGFWnsglFramework == NULL)
+			RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
+
+		CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII);
+
+		void* symbol = CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName);
+
+		CFRelease(symbolName);
+
+		return symbol;
+	}
+#endif
+
+	CVReturn displayCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { 
+		RGFW_UNUSED(displayLink) RGFW_UNUSED(inNow) RGFW_UNUSED(inOutputTime) RGFW_UNUSED(flagsIn) RGFW_UNUSED(flagsOut) RGFW_UNUSED(displayLinkContext)
+		return kCVReturnSuccess; 
+	}
+
+	id NSWindow_delegate(RGFW_window* win) {
+		return (id) objc_msgSend_id(win->src.window, sel_registerName("delegate"));
+	}
+
+	u32 RGFW_OnClose(void* self) {
+		RGFW_window* win = NULL;
+		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
+		if (win == NULL)
+			return true;
+
+		win->event.type = RGFW_quit;
+		RGFW_windowQuitCallback(win);
+
+		return true;
+	}
+
+	/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */
+	bool acceptsFirstResponder(void) { return true; }
+	bool performKeyEquivalent(NSEvent* event) { RGFW_UNUSED(event); return true; }
+
+	NSDragOperation draggingEntered(id self, SEL sel, id sender) { 
+		RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);  
+
+		return NSDragOperationCopy; 
+	}
+	NSDragOperation draggingUpdated(id self, SEL sel, id sender) { 
+		RGFW_UNUSED(sel); 
+
+		RGFW_window* win = NULL;
+		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
+		if (win == NULL)
+			return 0;
+		
+		if (!(win->_winArgs & RGFW_ALLOW_DND)) {
+			return 0;
+		}
+
+		win->event.type = RGFW_dnd_init;
+		win->src.dndPassed = 0;
+
+		NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
+
+		win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
+		RGFW_dndInitCallback(win, win->event.point);
+
+		return NSDragOperationCopy; 
+	}
+	bool prepareForDragOperation(id self) {
+		RGFW_window* win = NULL;
+		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
+		if (win == NULL)
+			return true;
+		
+		if (!(win->_winArgs & RGFW_ALLOW_DND)) {
+			return false;
+		}
+
+		return true;
+	}
+
+	void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);  return; }
+
+	/* NOTE(EimaMei): Usually, you never need 'id self, SEL cmd' for C -> Obj-C methods. This isn't the case. */
+	bool performDragOperation(id self, SEL sel, id sender) {
+		RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); 
+
+		RGFW_window* win = NULL;
+		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
+
+        if (win == NULL)
+			return false;
+
+		// NSPasteboard* pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard"));
+
+        /////////////////////////////
+        id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard"));
+
+        // Get the types of data available on the pasteboard
+        id types = objc_msgSend_id(pasteBoard, sel_registerName("types"));
+
+        // Get the string type for file URLs
+        id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType");
+
+        // Check if the pasteboard contains file URLs
+        if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) {
+		    #ifdef RGFW_DEBUG
+            printf("No files found on the pasteboard.\n");
+			#endif
+
+			return 0;
+		}
+
+		id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType);
+		int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count"));
+
+		if (count == 0)
+			return 0;
+
+		for (int i = 0; i < count; i++) {
+			id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i);
+			const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String"));
+			strncpy(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH);
+			win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0';
+		}
+		win->event.droppedFilesCount = count;
+
+		win->event.type = RGFW_dnd;
+		win->src.dndPassed = 0;
+		
+		NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
+		win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
+		
+		RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
+	
+    	return false;
+	}
+
+	static void NSMoveToResourceDir(void) {
+		/* sourced from glfw */
+		char resourcesPath[255];
+
+		CFBundleRef bundle = CFBundleGetMainBundle();
+		if (!bundle)
+			return;
+
+		CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
+		CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);
+
+		if (
+			CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo ||
+			CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0
+			) {
+			CFRelease(last);
+			CFRelease(resourcesURL);
+			return;
+		}
+
+		CFRelease(last);
+		CFRelease(resourcesURL);
+
+		chdir(resourcesPath);
+	}
+
+
+	NSSize RGFW__osxWindowResize(void* self, SEL sel, NSSize frameSize) {
+		RGFW_UNUSED(sel); 
+
+		RGFW_window* win = NULL;
+		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
+		if (win == NULL)
+			return frameSize;
+		
+		win->r.w = frameSize.width;
+		win->r.h = frameSize.height;
+		win->event.type = RGFW_windowResized;
+		RGFW_windowResizeCallback(win, win->r);
+		return frameSize;
+	}
+
+	void RGFW__osxWindowMove(void* self, SEL sel) {
+		RGFW_UNUSED(sel); 
+
+		RGFW_window* win = NULL;
+		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
+		if (win == NULL)
+			return;
+		
+		NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)(win->src.window, sel_registerName("frame"));
+		win->r.x = (i32) frame.origin.x;
+		win->r.y = (i32) frame.origin.y;
+
+		win->event.type = RGFW_windowMoved;
+		RGFW_windowMoveCallback(win, win->r);
+	}
+
+	void RGFW__osxUpdateLayer(void* self, SEL sel) {
+		RGFW_UNUSED(sel);
+
+		RGFW_window* win = NULL;
+		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
+		if (win == NULL)
+			return;
+		
+		win->event.type = RGFW_windowRefresh;
+		RGFW_windowRefreshCallback(win);
+	}
+
+	RGFWDEF void RGFW_init_buffer(RGFW_window* win);
+	void RGFW_init_buffer(RGFW_window* win) {
+		#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+			if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
+				RGFW_bufferSize = RGFW_getScreenSize();
+				
+			win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4);
+
+		#ifdef RGFW_OSMESA
+				win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
+				OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
+		#endif
+		#else
+		RGFW_UNUSED(win); /*!< if buffer rendering is not being used */
+		#endif
+	}
+
+
+	void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer) {
+		objc_msgSend_void_id(win->src.view, sel_registerName("setLayer"), layer);
+	}
+
+	void* RGFW_cocoaGetLayer(void) {
+		return objc_msgSend_class(objc_getClass("CAMetalLayer"), sel_registerName("layer"));
+	}
+
+
+	NSPasteboardType const NSPasteboardTypeURL = "public.url";
+	NSPasteboardType const NSPasteboardTypeFileURL  = "public.file-url";
+
+	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
+		static u8 RGFW_loaded = 0;
+
+		/* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea???
+		Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance).
+		*/
+		si_func_to_SEL_with_name("NSObject", "windowShouldClose", RGFW_OnClose);
+
+		/* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */
+		si_func_to_SEL("NSWindow", acceptsFirstResponder);
+		si_func_to_SEL("NSWindow", performKeyEquivalent);
+
+		// RR Create an autorelease pool
+		id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+		pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+		if (NSApp == NULL) {
+			NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
+
+			((void (*)(id, SEL, NSUInteger))objc_msgSend)
+				(NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
+		}
+
+		RGFW_window* win = RGFW_window_basic_init(rect, args);
+		
+		RGFW_window_setMouseDefault(win);
+
+		NSRect windowRect;
+		windowRect.origin.x = win->r.x;
+		windowRect.origin.y = win->r.y;
+		windowRect.size.width = win->r.w;
+		windowRect.size.height = win->r.h;
+
+		NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled;
+
+		if (!(args & RGFW_NO_RESIZE))
+			macArgs |= NSWindowStyleMaskResizable;
+		if (!(args & RGFW_NO_BORDER))
+			macArgs |= NSWindowStyleMaskTitled;
+		else
+			macArgs = NSWindowStyleMaskBorderless;
+		{
+			void* nsclass = objc_getClass("NSWindow");
+			void* func = sel_registerName("initWithContentRect:styleMask:backing:defer:");
+
+			win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend)
+				(NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false);
+		}
+
+		NSString* str = NSString_stringWithUTF8String(name);
+		objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str);
+
+#ifdef RGFW_EGL
+		if ((args & RGFW_NO_INIT_API) == 0)
+			RGFW_createOpenGLContext(win);
+#endif
+
+#ifdef RGFW_OPENGL
+	if ((args & RGFW_NO_INIT_API) == 0) {
+		void* attrs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE);
+		void* format = NSOpenGLPixelFormat_initWithAttributes(attrs);
+
+		if (format == NULL) {
+			printf("Failed to load pixel format for OpenGL\n");
+
+			void* attrs = RGFW_initFormatAttribs(1);
+			format = NSOpenGLPixelFormat_initWithAttributes(attrs);
+			if (format == NULL)
+				printf("and loading software rendering OpenGL failed\n");
+			else
+				printf("Switching to software rendering\n");
+		}
+		
+		/* the pixel format can be passed directly to opengl context creation to create a context 
+			this is because the format also includes information about the opengl version (which may be a bad thing) */
+		win->src.view = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {win->r.w, win->r.h}}, format);
+		objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL"));
+		win->src.ctx = objc_msgSend_id(win->src.view, sel_registerName("openGLContext"));
+	} else
+#endif
+	{
+		NSRect contentRect = (NSRect){{0, 0}, {win->r.w, win->r.h}};
+		win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend)
+			(NSAlloc((id)objc_getClass("NSView")), sel_registerName("initWithFrame:"),
+				contentRect);
+	}
+
+		void* contentView = NSWindow_contentView(win->src.window);
+		objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true);
+
+		objc_msgSend_void_id(win->src.window, sel_registerName("setContentView:"), win->src.view);
+
+#ifdef RGFW_OPENGL
+		if ((args & RGFW_NO_INIT_API) == 0)
+			objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext"));
+#endif
+		if (args & RGFW_TRANSPARENT_WINDOW) {
+#ifdef RGFW_OPENGL
+		if ((args & RGFW_NO_INIT_API) == 0) {
+			i32 opacity = 0;
+			#define NSOpenGLCPSurfaceOpacity 236
+			NSOpenGLContext_setValues(win->src.ctx, &opacity, NSOpenGLCPSurfaceOpacity);
+		}
+#endif
+
+			objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false);
+
+			objc_msgSend_void_id(win->src.window, sel_registerName("setBackgroundColor:"),
+				NSColor_colorWithSRGB(0, 0, 0, 0));
+		}
+
+		win->src.display = CGMainDisplayID();
+		CVDisplayLinkCreateWithCGDisplay(win->src.display, (CVDisplayLinkRef*)&win->src.displayLink);
+		CVDisplayLinkSetOutputCallback(win->src.displayLink, displayCallback, win);
+		CVDisplayLinkStart(win->src.displayLink);
+
+		RGFW_init_buffer(win);
+
+		#ifndef RGFW_NO_MONITOR
+		if (args & RGFW_SCALE_TO_MONITOR)
+			RGFW_window_scaleToMonitor(win);
+		#endif
+
+		if (args & RGFW_CENTER) {
+			RGFW_area screenR = RGFW_getScreenSize();
+			RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2));
+		}
+
+		if (args & RGFW_HIDE_MOUSE)
+			RGFW_window_showMouse(win, 0);
+
+		if (args & RGFW_COCOA_MOVE_TO_RESOURCE_DIR)
+			NSMoveToResourceDir();
+
+		Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);
+
+		class_addIvar(
+			delegateClass, "RGFW_window",
+			sizeof(RGFW_window*), rint(log2(sizeof(RGFW_window*))),
+			"L"
+		);
+
+		class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) RGFW__osxWindowResize, "{NSSize=ff}@:{NSSize=ff}");
+		class_addMethod(delegateClass, sel_registerName("updateLayer:"), (IMP) RGFW__osxUpdateLayer, "");
+		class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, "");
+		class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, "");
+		class_addMethod(delegateClass, sel_registerName("draggingEntered:"), (IMP)draggingEntered, "l@:@");
+		class_addMethod(delegateClass, sel_registerName("draggingUpdated:"), (IMP)draggingUpdated, "l@:@");
+		class_addMethod(delegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
+		class_addMethod(delegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
+		class_addMethod(delegateClass, sel_registerName("prepareForDragOperation:"), (IMP)prepareForDragOperation, "B@:@");
+		class_addMethod(delegateClass, sel_registerName("performDragOperation:"), (IMP)performDragOperation, "B@:@");
+
+		id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init"));
+
+		object_setInstanceVariable(delegate, "RGFW_window", win);
+
+		objc_msgSend_void_id(win->src.window, sel_registerName("setDelegate:"), delegate);
+
+		if (args & RGFW_ALLOW_DND) {
+			win->_winArgs |= RGFW_ALLOW_DND;
+
+			NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString};
+			NSregisterForDraggedTypes(win->src.window, types, 3);
+		}
+
+		// Show the window
+		objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
+		((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
+		objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
+
+		if (!RGFW_loaded) {
+			objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow"));
+
+			RGFW_loaded = 1;
+		}
+
+		objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow"));
+
+		objc_msgSend_void(NSApp, sel_registerName("finishLaunching"));
+
+		if (RGFW_root == NULL)
+			RGFW_root = win;
+
+		NSRetain(win->src.window);
+		NSRetain(NSApp);
+
+		return win;
+	}
+
+	void RGFW_window_setBorder(RGFW_window* win, u8 border) {
+		NSBackingStoreType storeType = NSWindowStyleMaskBorderless;
+		if (!border) {
+			storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
+		}
+		if (!(win->_winArgs & RGFW_NO_RESIZE)) {
+			storeType |= NSWindowStyleMaskResizable;
+		}
+		
+		((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)(win->src.window, sel_registerName("setStyleMask:"), storeType);
+
+		objc_msgSend_void_bool(win->src.window, sel_registerName("setHasShadow:"), border);
+	}
+
+	RGFW_area RGFW_getScreenSize(void) {
+		static CGDirectDisplayID display = 0;
+
+		if (display == 0)
+			display = CGMainDisplayID();
+
+		return RGFW_AREA(CGDisplayPixelsWide(display), CGDisplayPixelsHigh(display));
+	}
+
+	RGFW_point RGFW_getGlobalMousePoint(void) {
+		assert(RGFW_root != NULL);
+
+		CGEventRef e = CGEventCreate(NULL);
+		CGPoint point = CGEventGetLocation(e);
+		CFRelease(e);
+
+		return RGFW_POINT((u32) point.x, (u32) point.y); /*!< the point is loaded during event checks */
+	}
+
+	RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
+		NSPoint p =  ((NSPoint(*)(id, SEL)) objc_msgSend)(win->src.window, sel_registerName("mouseLocationOutsideOfEventStream"));
+
+		return RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
+	}
+
+	u32 RGFW_keysPressed[10]; /*10 keys at a time*/
+	typedef NS_ENUM(u32, NSEventType) {        /* various types of events */
+		NSEventTypeLeftMouseDown = 1,
+			NSEventTypeLeftMouseUp = 2,
+			NSEventTypeRightMouseDown = 3,
+			NSEventTypeRightMouseUp = 4,
+			NSEventTypeMouseMoved = 5,
+			NSEventTypeLeftMouseDragged = 6,
+			NSEventTypeRightMouseDragged = 7,
+			NSEventTypeMouseEntered = 8,
+			NSEventTypeMouseExited = 9,
+			NSEventTypeKeyDown = 10,
+			NSEventTypeKeyUp = 11,
+			NSEventTypeFlagsChanged = 12,
+			NSEventTypeAppKitDefined = 13,
+			NSEventTypeSystemDefined = 14,
+			NSEventTypeApplicationDefined = 15,
+			NSEventTypePeriodic = 16,
+			NSEventTypeCursorUpdate = 17,
+			NSEventTypeScrollWheel = 22,
+			NSEventTypeTabletPoint = 23,
+			NSEventTypeTabletProximity = 24,
+			NSEventTypeOtherMouseDown = 25,
+			NSEventTypeOtherMouseUp = 26,
+			NSEventTypeOtherMouseDragged = 27,
+			/* The following event types are available on some hardware on 10.5.2 and later */
+			NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29,
+			NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30,
+			NSEventTypeSwipe   API_AVAILABLE(macos(10.5)) = 31,
+			NSEventTypeRotate  API_AVAILABLE(macos(10.5)) = 18,
+			NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19,
+			NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20,
+
+			NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32,
+			NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33,
+
+			NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34,
+			NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37,
+
+			NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38,
+	};
+
+	typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */
+		NSEventMaskLeftMouseDown = 1ULL << NSEventTypeLeftMouseDown,
+			NSEventMaskLeftMouseUp = 1ULL << NSEventTypeLeftMouseUp,
+			NSEventMaskRightMouseDown = 1ULL << NSEventTypeRightMouseDown,
+			NSEventMaskRightMouseUp = 1ULL << NSEventTypeRightMouseUp,
+			NSEventMaskMouseMoved = 1ULL << NSEventTypeMouseMoved,
+			NSEventMaskLeftMouseDragged = 1ULL << NSEventTypeLeftMouseDragged,
+			NSEventMaskRightMouseDragged = 1ULL << NSEventTypeRightMouseDragged,
+			NSEventMaskMouseEntered = 1ULL << NSEventTypeMouseEntered,
+			NSEventMaskMouseExited = 1ULL << NSEventTypeMouseExited,
+			NSEventMaskKeyDown = 1ULL << NSEventTypeKeyDown,
+			NSEventMaskKeyUp = 1ULL << NSEventTypeKeyUp,
+			NSEventMaskFlagsChanged = 1ULL << NSEventTypeFlagsChanged,
+			NSEventMaskAppKitDefined = 1ULL << NSEventTypeAppKitDefined,
+			NSEventMaskSystemDefined = 1ULL << NSEventTypeSystemDefined,
+			NSEventMaskApplicationDefined = 1ULL << NSEventTypeApplicationDefined,
+			NSEventMaskPeriodic = 1ULL << NSEventTypePeriodic,
+			NSEventMaskCursorUpdate = 1ULL << NSEventTypeCursorUpdate,
+			NSEventMaskScrollWheel = 1ULL << NSEventTypeScrollWheel,
+			NSEventMaskTabletPoint = 1ULL << NSEventTypeTabletPoint,
+			NSEventMaskTabletProximity = 1ULL << NSEventTypeTabletProximity,
+			NSEventMaskOtherMouseDown = 1ULL << NSEventTypeOtherMouseDown,
+			NSEventMaskOtherMouseUp = 1ULL << NSEventTypeOtherMouseUp,
+			NSEventMaskOtherMouseDragged = 1ULL << NSEventTypeOtherMouseDragged,
+			/* The following event masks are available on some hardware on 10.5.2 and later */
+			NSEventMaskGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeGesture,
+			NSEventMaskMagnify API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeMagnify,
+			NSEventMaskSwipe API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeSwipe,
+			NSEventMaskRotate API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeRotate,
+			NSEventMaskBeginGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeBeginGesture,
+			NSEventMaskEndGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeEndGesture,
+
+			/* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit.
+			 */
+			NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) = 1ULL << NSEventTypeSmartMagnify,
+			NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) = 1ULL << NSEventTypePressure,
+			NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) = 1ULL << NSEventTypeDirectTouch,
+
+			NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) = 1ULL << NSEventTypeChangeMode,
+
+			NSEventMaskAny = ULONG_MAX,
+
+	};
+
+	typedef enum NSEventModifierFlags {
+		NSEventModifierFlagCapsLock = 1 << 16,
+		NSEventModifierFlagShift = 1 << 17,
+		NSEventModifierFlagControl = 1 << 18,
+		NSEventModifierFlagOption = 1 << 19,
+		NSEventModifierFlagCommand = 1 << 20,
+		NSEventModifierFlagNumericPad = 1 << 21
+	} NSEventModifierFlags;
+
+	void RGFW_stopCheckEvents(void) { 
+		id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+        eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+		NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend)
+			(NSApp, sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"), 
+				NSEventTypeApplicationDefined, (NSPoint){0, 0}, 0, 0, 0, NULL, 0, 0, 0);
+
+		((void (*)(id, SEL, id, bool))objc_msgSend)
+			(NSApp, sel_registerName("postEvent:atStart:"), e, 1);
+
+		objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+	}
+
+	void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
+		RGFW_UNUSED(win);
+		
+		id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+        eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+		void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend)
+					(objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS);
+
+		NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend)
+			(NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), 
+				ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
+
+
+		if (e) {
+			objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
+		}
+
+		objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+	}
+
+	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
+		assert(win != NULL);
+		
+		if (win->event.type == RGFW_quit)
+			return NULL;
+		
+		if ((win->event.type == RGFW_dnd || win->event.type == RGFW_dnd_init) && win->src.dndPassed == 0) {
+			win->src.dndPassed = 1;
+			return &win->event;
+		}
+
+		id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+        eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+		static void* eventFunc = NULL;
+		if (eventFunc == NULL) 
+			eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
+
+		if ((win->event.type == RGFW_windowMoved || win->event.type == RGFW_windowResized || win->event.type == RGFW_windowRefresh) && win->event.keyCode != 120) {
+			win->event.keyCode = 120;
+			objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+			return &win->event;
+		}
+
+		void* date = NULL;
+
+		NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend)
+			(NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
+
+		if (e == NULL) {
+			objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+			return NULL;
+		}
+		
+		if (objc_msgSend_id(e, sel_registerName("window")) != win->src.window) {
+			((void (*)(id, SEL, id, bool))objc_msgSend)
+				(NSApp, sel_registerName("postEvent:atStart:"), e, 0);
+						
+			objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+			return NULL;
+		}
+
+		if (win->event.droppedFilesCount) {
+			u32 i;
+			for (i = 0; i < win->event.droppedFilesCount; i++)
+				win->event.droppedFiles[i][0] = '\0';
+		}
+
+		win->event.droppedFilesCount = 0;
+		win->event.type = 0;
+		
+		switch (objc_msgSend_uint(e, sel_registerName("type"))) {
+			case NSEventTypeMouseEntered: {
+				win->event.type = RGFW_mouseEnter;
+				NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
+
+				win->event.point = RGFW_POINT((i32) p.x, (i32) (win->r.h - p.y));
+				RGFW_mouseNotifyCallBack(win, win->event.point, 1);
+				break;
+			}
+			
+			case NSEventTypeMouseExited:
+				win->event.type = RGFW_mouseLeave;
+				RGFW_mouseNotifyCallBack(win, win->event.point, 0);
+				break;
+
+			case NSEventTypeKeyDown: {
+				u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
+				win->event.keyCode = RGFW_apiKeyCodeToRGFW(key);
+				RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current;
+
+				win->event.type = RGFW_keyPressed;
+				char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters")));
+				strncpy(win->event.keyName, str, 16);
+				win->event.repeat = RGFW_isPressed(win, win->event.keyCode);
+				RGFW_keyboard[win->event.keyCode].current = 1;
+
+				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1);
+				break;
+			}
+
+			case NSEventTypeKeyUp: {
+				u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
+				win->event.keyCode = RGFW_apiKeyCodeToRGFW(key);;
+
+				RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current;
+
+				win->event.type = RGFW_keyReleased;
+				char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters")));
+				strncpy(win->event.keyName, str, 16);
+
+				RGFW_keyboard[win->event.keyCode].current = 0;
+				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0);
+				break;
+			}
+
+			case NSEventTypeFlagsChanged: {
+				u32 flags = objc_msgSend_uint(e, sel_registerName("modifierFlags"));
+				RGFW_updateLockState(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255));
+				
+				u8 i;
+				for (i = 0; i < 9; i++)
+					RGFW_keyboard[i + RGFW_CapsLock].prev = 0;
+				
+				for (i = 0; i < 5; i++) {
+					u32 shift = (1 << (i + 16));
+					u32 key = i + RGFW_CapsLock;
+
+					if ((flags & shift) && !RGFW_wasPressed(win, key)) {
+						RGFW_keyboard[key].current = 1;
+
+						if (key != RGFW_CapsLock)
+							RGFW_keyboard[key+ 4].current = 1;
+						
+						win->event.type = RGFW_keyPressed;
+						win->event.keyCode = key;
+						break;
+					} 
+					
+					if (!(flags & shift) && RGFW_wasPressed(win, key)) {
+						RGFW_keyboard[key].current = 0;
+						
+						if (key != RGFW_CapsLock)
+							RGFW_keyboard[key + 4].current = 0;
+
+						win->event.type = RGFW_keyReleased;
+						win->event.keyCode = key;
+						break;
+					}
+				}
+
+				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, win->event.type == RGFW_keyPressed);
+
+				break;
+			}
+			case NSEventTypeLeftMouseDragged:
+			case NSEventTypeOtherMouseDragged:
+			case NSEventTypeRightMouseDragged:
+			case NSEventTypeMouseMoved:
+				win->event.type = RGFW_mousePosChanged;
+				NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
+				win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
+
+				if ((win->_winArgs & RGFW_HOLD_MOUSE)) {
+					p.x = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaX"));
+					p.y = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY"));
+					
+					win->event.point = RGFW_POINT((i32)p.x, (i32)p.y);
+				}
+
+				RGFW_mousePosCallback(win, win->event.point);
+				break;
+
+			case NSEventTypeLeftMouseDown:
+				win->event.button = RGFW_mouseLeft;
+				win->event.type = RGFW_mouseButtonPressed;
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 1;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+				break;
+
+			case NSEventTypeOtherMouseDown:
+				win->event.button = RGFW_mouseMiddle;
+				win->event.type = RGFW_mouseButtonPressed;
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 1;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+				break;
+
+			case NSEventTypeRightMouseDown:
+				win->event.button = RGFW_mouseRight;
+				win->event.type = RGFW_mouseButtonPressed;
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 1;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+				break;
+
+			case NSEventTypeLeftMouseUp:
+				win->event.button = RGFW_mouseLeft;
+				win->event.type = RGFW_mouseButtonReleased;
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 0;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
+				break;
+
+			case NSEventTypeOtherMouseUp:
+				win->event.button = RGFW_mouseMiddle;
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 0;
+				win->event.type = RGFW_mouseButtonReleased;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
+				break;
+
+			case NSEventTypeRightMouseUp:
+				win->event.button = RGFW_mouseRight;
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 0;
+				win->event.type = RGFW_mouseButtonReleased;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
+				break;
+
+			case NSEventTypeScrollWheel: {
+				double deltaY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY"));
+
+				if (deltaY > 0) {
+					win->event.button = RGFW_mouseScrollUp;
+				}
+				else if (deltaY < 0) {
+					win->event.button = RGFW_mouseScrollDown;
+				}
+
+				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+				RGFW_mouseButtons[win->event.button].current = 1;
+
+				win->event.scroll = deltaY;
+
+				win->event.type = RGFW_mouseButtonPressed;
+				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+				break;
+			}
+
+			default:
+				break;
+		}
+
+		objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
+		((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
+				
+		objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+		return &win->event;
+	}
+
+
+	void RGFW_window_move(RGFW_window* win, RGFW_point v) {
+		assert(win != NULL);
+
+		win->r.x = v.x;
+		win->r.y = v.y;
+		((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
+			(win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true);
+	}
+
+	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
+		assert(win != NULL);
+
+		win->r.w = a.w;
+		win->r.h = a.h;
+		((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
+			(win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true);
+	}
+
+	void RGFW_window_minimize(RGFW_window* win) {
+		assert(win != NULL);
+
+		objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL);
+	}
+
+	void RGFW_window_restore(RGFW_window* win) {
+		assert(win != NULL);
+
+		objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL);
+	}
+
+	void RGFW_window_setName(RGFW_window* win, char* name) {
+		assert(win != NULL);
+
+		NSString* str = NSString_stringWithUTF8String(name);
+		objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str);
+	}
+
+	#ifndef RGFW_NO_PASSTHROUGH
+	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
+		objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough);
+	}
+	#endif
+
+	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
+		if (a.w == 0 && a.h == 0)
+			return;
+
+		((void (*)(id, SEL, NSSize))objc_msgSend)
+			(win->src.window, sel_registerName("setMinSize:"), (NSSize){a.w, a.h});
+	}
+
+	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
+		if (a.w == 0 && a.h == 0)
+			return;
+
+		((void (*)(id, SEL, NSSize))objc_msgSend)
+			(win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h});
+	}
+
+	void RGFW_window_setIcon(RGFW_window* win, u8* data, RGFW_area area, i32 channels) {
+		assert(win != NULL);
+
+		/* code by EimaMei  */
+		// Make a bitmap representation, then copy the loaded image into it.
+		void* representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * channels, 8 * channels);
+		memcpy(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * channels);
+
+		// Add ze representation.
+		void* dock_image = NSImage_initWithSize((NSSize){area.w, area.h});
+		NSImage_addRepresentation(dock_image, (void*) representation);
+
+		// Finally, set the dock image to it.
+		objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image);
+		// Free the garbage.
+		release(dock_image);
+		release(representation);
+	}
+
+	NSCursor* NSCursor_arrowStr(char* str) {
+		void* nclass = objc_getClass("NSCursor");
+		void* func = sel_registerName(str);
+		return (NSCursor*) objc_msgSend_id(nclass, func);
+	}
+
+	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
+		assert(win != NULL);
+
+		if (image == NULL) {
+			objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set"));
+			return;
+		}
+
+		/* NOTE(EimaMei): Code by yours truly. */
+		// Make a bitmap representation, then copy the loaded image into it.
+		void* representation = NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * channels, 8 * channels);
+		memcpy(NSBitmapImageRep_bitmapData(representation), image, a.w * a.h * channels);
+
+		// Add ze representation.
+		void* cursor_image = NSImage_initWithSize((NSSize){a.w, a.h});
+		NSImage_addRepresentation(cursor_image, representation);
+
+		// Finally, set the cursor image.
+		void* cursor = NSCursor_initWithImage(cursor_image, (NSPoint){0.0, 0.0});
+
+		objc_msgSend_void(cursor, sel_registerName("set"));
+
+		// Free the garbage.
+		release(cursor_image);
+		release(representation);
+	}
+
+	void RGFW_window_setMouseDefault(RGFW_window* win) {
+		RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW);
+	}
+
+	void RGFW_window_showMouse(RGFW_window* win, i8 show) {
+		RGFW_UNUSED(win);
+
+		if (show) {
+			CGDisplayShowCursor(kCGDirectMainDisplay);
+		}
+		else {
+			CGDisplayHideCursor(kCGDirectMainDisplay);
+		}
+	}
+
+	void RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouses) {
+		if (stdMouses > ((sizeof(RGFW_mouseIconSrc)) / (sizeof(char*))))
+			return;
+		
+		char* mouseStr = RGFW_mouseIconSrc[stdMouses];
+		void* mouse = NSCursor_arrowStr(mouseStr);
+
+		if (mouse == NULL)
+			return;
+
+		RGFW_UNUSED(win);
+		CGDisplayShowCursor(kCGDirectMainDisplay);
+		objc_msgSend_void(mouse, sel_registerName("set"));
+	}
+	
+	void RGFW_releaseCursor(RGFW_window* win) {
+		RGFW_UNUSED(win);
+		CGAssociateMouseAndMouseCursorPosition(1);	
+	}
+
+	void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { 
+		RGFW_UNUSED(win)
+
+		CGWarpMouseCursorPosition(CGPointMake(r.x + (r.w / 2), r.y + (r.h / 2)));
+		CGAssociateMouseAndMouseCursorPosition(0);
+	}
+
+	void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) {
+		RGFW_UNUSED(win);
+		
+		CGWarpMouseCursorPosition(CGPointMake(v.x, v.y));		
+	}
+
+
+	void RGFW_window_hide(RGFW_window* win) {
+		objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false);
+	}
+
+	void RGFW_window_show(RGFW_window* win) {
+		((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
+		objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
+	}
+
+	u8 RGFW_window_isFullscreen(RGFW_window* win) {
+		assert(win != NULL);
+
+		NSWindowStyleMask mask = (NSWindowStyleMask) objc_msgSend_uint(win->src.window, sel_registerName("styleMask"));
+		return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
+	}
+
+	u8 RGFW_window_isHidden(RGFW_window* win) {
+		assert(win != NULL);
+
+		bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible"));
+		return visible == NO && !RGFW_window_isMinimized(win);
+	}
+
+	u8 RGFW_window_isMinimized(RGFW_window* win) {
+		assert(win != NULL);
+
+		return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES;
+	}
+
+	u8 RGFW_window_isMaximized(RGFW_window* win) {
+		assert(win != NULL);
+
+		return objc_msgSend_bool(win->src.window, sel_registerName("isZoomed"));
+	}
+
+	static RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display) {
+		RGFW_monitor monitor;
+
+		CGRect bounds = CGDisplayBounds(display);
+		monitor.rect = RGFW_RECT((int) bounds.origin.x, (int) bounds.origin.y, (int) bounds.size.width, (int) bounds.size.height);
+
+		CGSize screenSizeMM = CGDisplayScreenSize(display);
+		monitor.physW = screenSizeMM.width;
+		monitor.physH = screenSizeMM.height;
+
+		monitor.scaleX = ((monitor.rect.w / (screenSizeMM.width / 25.4)) / 96) + 0.25;
+		monitor.scaleY = ((monitor.rect.h / (screenSizeMM.height / 25.4)) / 96) + 0.25;
+
+		return monitor;
+	}
+
+
+	static RGFW_monitor RGFW_monitors[7];
+
+	RGFW_monitor* RGFW_getMonitors(void) {
+		static CGDirectDisplayID displays[7];
+		u32 count;
+
+		if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess)
+			return NULL;
+
+		for (u32 i = 0; i < count; i++)
+			RGFW_monitors[i] = RGFW_NSCreateMonitor(displays[i]);
+
+		return RGFW_monitors;
+	}
+
+	RGFW_monitor RGFW_getPrimaryMonitor(void) {
+		CGDirectDisplayID primary = CGMainDisplayID();
+		return RGFW_NSCreateMonitor(primary);
+	}
+
+	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
+		return RGFW_NSCreateMonitor(win->src.display);
+	}
+
+	char* RGFW_readClipboard(size_t* size) {
+		char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString);
+		
+		size_t clip_len = 1;
+
+		if (clip != NULL) {
+			clip_len = strlen(clip) + 1; 
+		}
+
+		char* str = (char*)RGFW_MALLOC(sizeof(char) * clip_len);
+		
+		if (clip != NULL) {
+			strncpy(str, clip, clip_len);
+		}
+
+		str[clip_len] = '\0';
+		
+		if (size != NULL)
+			*size = clip_len;
+		return str;
+	}
+
+	void RGFW_writeClipboard(const char* text, u32 textLen) {
+		RGFW_UNUSED(textLen);
+
+		NSPasteboardType array[] = { NSPasteboardTypeString, NULL };
+		NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL);
+
+		NSPasteBoard_setString(NSPasteboard_generalPasteboard(), text, NSPasteboardTypeString);
+	}
+
+	u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) {
+		RGFW_UNUSED(jsNumber);
+
+		assert(win != NULL);
+
+		return RGFW_registerJoystickF(win, (char*) "");
+	}
+
+	u16 RGFW_registerJoystickF(RGFW_window* win, char* file) {
+		RGFW_UNUSED(file);
+
+		assert(win != NULL);
+
+		return RGFW_joystickCount - 1;
+	}
+
+	#ifdef RGFW_OPENGL
+	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+		assert(win != NULL);
+		objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext"));
+	}
+	#endif
+
+	#if !defined(RGFW_EGL)
+	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
+		assert(win != NULL);
+		#if defined(RGFW_OPENGL)
+		
+		NSOpenGLContext_setValues(win->src.ctx, &swapInterval, 222);
+		#else
+		RGFW_UNUSED(swapInterval);
+		#endif
+	}
+	#endif
+	
+	// Function to create a CGImageRef from an array of bytes
+	CGImageRef createImageFromBytes(unsigned char *buffer, int width, int height)
+	{
+		// Define color space
+        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+        // Create bitmap context
+        CGContextRef context = CGBitmapContextCreate(
+        		buffer, 
+        		width, height,
+        		8,
+        		RGFW_bufferSize.w * 4, 
+        		colorSpace,
+        		kCGImageAlphaPremultipliedLast);
+        // Create image from bitmap context
+        CGImageRef image = CGBitmapContextCreateImage(context);
+        // Release the color space and context
+        CGColorSpaceRelease(colorSpace);
+        CGContextRelease(context);
+                         
+        return image;
+    }
+
+	void RGFW_window_swapBuffers(RGFW_window* win) {
+		assert(win != NULL);
+		/* clear the window*/
+
+		if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) {
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+			#ifdef RGFW_OSMESA
+			RGFW_OSMesa_reorganize();
+			#endif
+
+			void* view = NSWindow_contentView(win->src.window);
+			void* layer = objc_msgSend_id(view, sel_registerName("layer"));
+
+			((void(*)(id, SEL, NSRect))objc_msgSend)(layer,
+				sel_registerName("setFrame:"),
+				(NSRect){{0, 0}, {win->r.w, win->r.h}});
+
+            CGImageRef image = createImageFromBytes(win->buffer, win->r.w, win->r.h);
+            // Get the current graphics context
+            id graphicsContext = objc_msgSend_class(objc_getClass("NSGraphicsContext"), sel_registerName("currentContext"));
+            // Get the CGContext from the current NSGraphicsContext
+            id cgContext = objc_msgSend_id(graphicsContext, sel_registerName("graphicsPort"));
+			// Draw the image in the context
+			NSRect bounds = (NSRect){{0,0}, {win->r.w, win->r.h}};
+		    CGContextDrawImage((void*)cgContext, *(CGRect*)&bounds, image);
+          	// Flush the graphics context to ensure the drawing is displayed
+            objc_msgSend_id(graphicsContext, sel_registerName("flushGraphics"));
+            
+            objc_msgSend_void_id(layer, sel_registerName("setContents:"), (id)image);
+            objc_msgSend_id(layer, sel_registerName("setNeedsDisplay"));
+            
+            CGImageRelease(image);
+#endif
+		}
+
+		if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) {
+			#ifdef RGFW_EGL
+					eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
+			#elif defined(RGFW_OPENGL)
+					objc_msgSend_void(win->src.ctx, sel_registerName("flushBuffer"));
+			#endif
+		}
+	}
+
+	void RGFW_window_close(RGFW_window* win) {
+		assert(win != NULL);
+		release(win->src.view);
+
+#ifdef RGFW_ALLOC_DROPFILES
+		{
+			u32 i;
+			for (i = 0; i < RGFW_MAX_DROPS; i++)
+				RGFW_FREE(win->event.droppedFiles[i]);
+
+
+			RGFW_FREE(win->event.droppedFiles);
+		}
+#endif
+	
+#ifdef RGFW_BUFFER
+		release(win->src.bitmap);
+		release(win->src.image);
+#endif
+
+		CVDisplayLinkStop(win->src.displayLink);
+		CVDisplayLinkRelease(win->src.displayLink);
+
+		RGFW_FREE(win);
+	}
+
+	u64 RGFW_getTimeNS(void) {
+		static mach_timebase_info_data_t timebase_info;
+		if (timebase_info.denom == 0) {
+			mach_timebase_info(&timebase_info);
+		}
+		return mach_absolute_time() * timebase_info.numer / timebase_info.denom;
+	}
+
+	u64 RGFW_getTime(void) {
+		static mach_timebase_info_data_t timebase_info;
+		if (timebase_info.denom == 0) {
+			mach_timebase_info(&timebase_info);
+		}
+		return (double) mach_absolute_time() * (double) timebase_info.numer / ((double) timebase_info.denom * 1e9);
+	}
+#endif /* RGFW_MACOS */
+
+/*
+	End of MaOS defines
+*/
+
+/*
+	WEBASM defines
+*/
+
+#ifdef RGFW_WEBASM
+RGFW_Event RGFW_events[20];
+size_t RGFW_eventLen = 0;
+
+EM_BOOL Emscripten_on_keydown(int eventType, const EmscriptenKeyboardEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+	
+	RGFW_events[RGFW_eventLen].type = RGFW_keyPressed;
+	memcpy(RGFW_events[RGFW_eventLen].keyName, e->key, 16);
+	RGFW_events[RGFW_eventLen].keyCode = RGFW_apiKeyCodeToRGFW(e->keyCode);
+	RGFW_events[RGFW_eventLen].lockState = 0;
+	RGFW_eventLen++;
+
+	RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current;
+	RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 1;
+	RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 1);
+	
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_keyup(int eventType, const EmscriptenKeyboardEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	RGFW_events[RGFW_eventLen].type = RGFW_keyReleased;
+	memcpy(RGFW_events[RGFW_eventLen].keyName, e->key, 16);
+	RGFW_events[RGFW_eventLen].keyCode = RGFW_apiKeyCodeToRGFW(e->keyCode);
+	RGFW_events[RGFW_eventLen].lockState = 0;
+	RGFW_eventLen++;
+
+	RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current;
+	RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 0;
+
+	RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 0);
+
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	RGFW_events[RGFW_eventLen].type = RGFW_windowResized;
+	RGFW_eventLen++;
+
+	RGFW_windowResizeCallback(RGFW_root, RGFW_RECT(0, 0, e->windowInnerWidth, e->windowInnerHeight));
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	RGFW_events[RGFW_eventLen].type = RGFW_windowResized;
+	RGFW_eventLen++;
+
+	RGFW_root->r = RGFW_RECT(0, 0, e->elementWidth, e->elementHeight);
+	RGFW_windowResizeCallback(RGFW_root, RGFW_root->r);
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e);
+
+	RGFW_events[RGFW_eventLen].type = RGFW_focusIn;
+	RGFW_eventLen++;
+
+	RGFW_root->event.inFocus = 1;
+	RGFW_focusCallback(RGFW_root, 1);
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e);
+
+	RGFW_events[RGFW_eventLen].type = RGFW_focusOut;
+	RGFW_eventLen++;
+
+	RGFW_root->event.inFocus = 0;
+	RGFW_focusCallback(RGFW_root, 0);
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	RGFW_events[RGFW_eventLen].type = RGFW_mousePosChanged;
+
+	if ((RGFW_root->_winArgs & RGFW_HOLD_MOUSE)) {
+		RGFW_point p = RGFW_POINT(e->movementX, e->movementY);
+		RGFW_events[RGFW_eventLen].point = p;
+	}
+	else
+		RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY);
+	RGFW_eventLen++;
+	
+	RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point);
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed;
+	RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY);
+	RGFW_events[RGFW_eventLen].button = e->button + 1; 
+	RGFW_events[RGFW_eventLen].scroll = 0;
+
+	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
+	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1;
+
+	RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1);
+	RGFW_eventLen++;
+	
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased;
+	RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY);
+	RGFW_events[RGFW_eventLen].button = e->button + 1; 
+	RGFW_events[RGFW_eventLen].scroll = 0;
+
+	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
+	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0;
+
+	RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0);
+	RGFW_eventLen++;
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed;
+	RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->mouse.targetX, e->mouse.targetY);
+	RGFW_events[RGFW_eventLen].button = RGFW_mouseScrollUp + (e->deltaY < 0); 
+	RGFW_events[RGFW_eventLen].scroll = e->deltaY;
+
+	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
+	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1;
+
+	RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1);
+	RGFW_eventLen++;
+
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* e, void* userData) { 
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+    size_t i; 
+    for (i = 0; i < (size_t)e->numTouches; i++) { 
+	    RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed;
+	    RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY);
+	    RGFW_events[RGFW_eventLen].button = 1; 
+	    RGFW_events[RGFW_eventLen].scroll = 0;
+
+
+	    RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
+	    RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1;
+
+        RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point);
+
+	    RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1);
+    	RGFW_eventLen++;
+    }
+
+	return EM_TRUE;
+}
+EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* e, void* userData) { 
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+    
+    size_t i; 
+    for (i = 0; i < (size_t)e->numTouches; i++) { 
+   	    RGFW_events[RGFW_eventLen].type = RGFW_mousePosChanged;
+	    RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY);
+
+        RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point);
+	    RGFW_eventLen++;
+    }
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* e, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+	
+    size_t i; 
+    for (i = 0; i < (size_t)e->numTouches; i++) { 
+	    RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased;
+	    RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY);
+	    RGFW_events[RGFW_eventLen].button = 1; 
+	    RGFW_events[RGFW_eventLen].scroll = 0;
+
+	    RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
+	    RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0;
+        
+	    RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0);
+	    RGFW_eventLen++;
+    }
+	return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); return EM_TRUE; }
+
+EM_BOOL Emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) {	
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	if (gamepadEvent->index >= 4)
+		return 0;
+
+	RGFW_joysticks[gamepadEvent->index] = gamepadEvent->connected;
+
+    return 1; // The event was consumed by the callback handler
+}
+
+void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) {
+	if (!(RGFW_root->_winArgs & RGFW_ALLOW_DND))
+		return;
+
+	RGFW_events[RGFW_eventLen].droppedFilesCount = count;	
+	RGFW_dndCallback(RGFW_root, RGFW_events[RGFW_eventLen].droppedFiles, count);
+	RGFW_eventLen++;
+}
+
+b8 RGFW_stopCheckEvents_bool = RGFW_FALSE;
+void RGFW_stopCheckEvents(void) { 
+	RGFW_stopCheckEvents_bool = RGFW_TRUE;
+}
+
+void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
+	RGFW_UNUSED(win);
+
+	if (waitMS == 0)
+		return;
+	
+	u32 start = (u32)(((u64)RGFW_getTimeNS()) / 1e+6);
+
+	while ((RGFW_eventLen == 0) && RGFW_stopCheckEvents_bool == RGFW_FALSE && 
+		(waitMS < 0 || (RGFW_getTimeNS() / 1e+6) - start < waitMS)
+	) {
+		emscripten_sleep(0);
+	}
+	
+	RGFW_stopCheckEvents_bool = RGFW_FALSE;
+}
+
+RGFWDEF void RGFW_init_buffer(RGFW_window* win);
+void RGFW_init_buffer(RGFW_window* win) {
+	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+		if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
+			RGFW_bufferSize = RGFW_getScreenSize();
+		
+		win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4);
+	#ifdef RGFW_OSMESA
+			win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
+			OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
+	#endif
+	#else
+	RGFW_UNUSED(win); /*!< if buffer rendering is not being used */
+	#endif
+}
+
+void EMSCRIPTEN_KEEPALIVE RGFW_makeSetValue(size_t index, char* file) { 
+	/* This seems like a terrible idea, don't replicate this unless you hate yourself or the OS */
+	/* TODO: find a better way to do this, 
+		strcpy doesn't seem to work, maybe because of asyncio
+	*/
+
+	RGFW_events[RGFW_eventLen].type = RGFW_dnd;
+	char** arr = (char**)&RGFW_events[RGFW_eventLen].droppedFiles[index];
+	*arr = file;
+}
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+void EMSCRIPTEN_KEEPALIVE RGFW_mkdir(char* name) { mkdir(name, 0755); }
+
+void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, size_t len) {
+    FILE* file = fopen(path, "w+");
+	if (file == NULL)
+		return;
+
+    fwrite(data, sizeof(char), len, file);
+    fclose(file);
+}
+
+RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
+	RGFW_UNUSED(name) 
+
+	RGFW_UNUSED(RGFW_initFormatAttribs);
+	
+    RGFW_window* win = RGFW_window_basic_init(rect, args);
+
+    EmscriptenWebGLContextAttributes attrs;
+    attrs.alpha = EM_TRUE;
+    attrs.depth = EM_TRUE;
+	attrs.alpha = EM_TRUE;
+    attrs.stencil = RGFW_STENCIL;
+    attrs.antialias = RGFW_SAMPLES;
+    attrs.premultipliedAlpha = EM_TRUE;
+    attrs.preserveDrawingBuffer = EM_FALSE;
+	
+    if (RGFW_DOUBLE_BUFFER == 0)
+        attrs.renderViaOffscreenBackBuffer = 0;
+    else
+        attrs.renderViaOffscreenBackBuffer = RGFW_AUX_BUFFERS;
+    
+    attrs.failIfMajorPerformanceCaveat = EM_FALSE;
+	attrs.majorVersion = (RGFW_majorVersion == 0) ? 1 : RGFW_majorVersion;
+	attrs.minorVersion = RGFW_minorVersion;
+	
+    attrs.enableExtensionsByDefault = EM_TRUE;
+    attrs.explicitSwapControl = EM_TRUE;
+
+    emscripten_webgl_init_context_attributes(&attrs);
+    win->src.ctx = emscripten_webgl_create_context("#canvas", &attrs);
+    emscripten_webgl_make_context_current(win->src.ctx);
+
+	#ifdef LEGACY_GL_EMULATION
+	EM_ASM("Module.useWebGL = true; GLImmediate.init();");	
+	#endif
+
+	emscripten_set_canvas_element_size("#canvas", rect.w, rect.h);
+	emscripten_set_window_title(name);
+
+	/* load callbacks */
+    emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keydown);
+    emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keyup);
+    emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_resize);
+    emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, Emscripten_on_fullscreenchange);
+    emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousemove);
+    emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchstart);
+    emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchend);
+    emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchmove);
+    emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchcancel);
+    emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousedown);
+    emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mouseup);
+    emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_wheel);
+    emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusin);
+    emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusout);
+	emscripten_set_gamepadconnected_callback(NULL, 1, Emscripten_on_gamepad);
+	emscripten_set_gamepaddisconnected_callback(NULL, 1, Emscripten_on_gamepad);
+	
+	if (args & RGFW_ALLOW_DND)  {
+		win->_winArgs |= RGFW_ALLOW_DND;
+	}
+
+    EM_ASM({
+		var canvas = document.getElementById('canvas');
+        canvas.addEventListener('drop', function(e) {
+            e.preventDefault();
+            if (e.dataTransfer.file < 0)
+				return;
+
+			var filenamesArray = [];
+			var count = e.dataTransfer.files.length;
+
+			/* Read and save the files to emscripten's files */
+			var drop_dir = '.rgfw_dropped_files';
+			Module._RGFW_mkdir(drop_dir);
+
+			for (var i = 0; i < count; i++) {
+				var file = e.dataTransfer.files[i];
+
+				var path = '/' + drop_dir + '/' + file.name.replace("//", '_');
+				var reader = new FileReader();
+				
+				reader.onloadend = (e) => {
+					if (reader.readyState != 2) {
+						out('failed to read dropped file: '+file.name+': '+reader.error);
+					}
+					else {
+						var data = e.target.result;
+						
+						_RGFW_writeFile(path, new Uint8Array(data), file.size);
+					}
+				};
+
+				reader.readAsArrayBuffer(file);		
+				// This works weird on modern opengl
+				var filename = stringToNewUTF8(path);
+
+				filenamesArray.push(filename);
+				
+				Module._RGFW_makeSetValue(i, filename);
+			}
+			
+			Module._Emscripten_onDrop(count);
+			
+			for (var i = 0; i < count; ++i) {
+				_free(filenamesArray[i]);
+			}
+        }, true);
+
+        canvas.addEventListener('dragover', function(e) { e.preventDefault(); return false; }, true);
+    });
+
+	RGFW_init_buffer(win);
+	glViewport(0, 0, rect.w, rect.h);
+	
+	RGFW_root = win; 
+
+	if (args & RGFW_HIDE_MOUSE) {
+		RGFW_window_showMouse(win, 0);
+	}
+
+	if (args & RGFW_FULLSCREEN) {
+		RGFW_window_resize(win, RGFW_getScreenSize());
+	}
+
+    return win;
+}
+
+RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
+	static u8 index = 0;
+	
+	if (index == 0) 
+		RGFW_resetKey();
+
+	/* check gamepads */
+    for (int i = 0; (i < emscripten_get_num_gamepads()) && (i < 4); i++) {
+		if (RGFW_joysticks[i] == 0)
+			continue;;
+		
+        EmscriptenGamepadEvent gamepadState;
+
+        if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS)
+			break;
+
+		// Register buttons data for every connected gamepad
+		for (int j = 0; (j < gamepadState.numButtons) && (j < 16); j++) {
+			u32 map[] = {
+				RGFW_JS_A, RGFW_JS_X, RGFW_JS_B, RGFW_JS_Y,
+				RGFW_JS_L1, RGFW_JS_R1, RGFW_JS_L2, RGFW_JS_R2,
+				RGFW_JS_SELECT, RGFW_JS_START,
+				0, 0,
+				RGFW_JS_UP, RGFW_JS_DOWN, RGFW_JS_LEFT, RGFW_JS_RIGHT
+			};
+
+			u32 button = map[j]; 
+			if (RGFW_jsPressed[i][button] != gamepadState.digitalButton[j]) {
+				win->event.type = RGFW_jsButtonPressed;
+				win->event.joystick = i;
+				win->event.button = map[j];
+				return &win->event;
+			}
+
+			RGFW_jsPressed[i][button] = gamepadState.digitalButton[j];
+		}
+
+		for (int j = 0; (j < gamepadState.numAxes) && (j < 4); j += 2) {
+			win->event.axisesCount = gamepadState.numAxes;
+			if (win->event.axis[j].x != gamepadState.axis[j] || 
+				win->event.axis[j].y != gamepadState.axis[j + 1]
+			) {
+				win->event.axis[j].x = gamepadState.axis[j];
+				win->event.axis[j].y = gamepadState.axis[j + 1];
+				win->event.type = RGFW_jsAxisMove;
+				win->event.joystick = i;
+				return &win->event;
+			}
+		}
+    }
+
+	/* check queued events */
+	if (RGFW_eventLen == 0)
+		return NULL;
+	
+	RGFW_events[index].frameTime = win->event.frameTime;
+	RGFW_events[index].frameTime2 = win->event.frameTime2;
+	RGFW_events[index].inFocus = win->event.inFocus;
+
+	win->event = RGFW_events[index];
+
+	RGFW_eventLen--;
+
+	if (RGFW_eventLen)
+		index++;
+	else
+		index = 0;
+
+	return &win->event;
+}
+
+void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
+	RGFW_UNUSED(win)
+	emscripten_set_canvas_element_size("#canvas", a.w, a.h);
+}
+
+/* NOTE: I don't know if this is possible */
+void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); }
+/* this one might be possible but it looks iffy */
+void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { RGFW_UNUSED(win); RGFW_UNUSED(channels) RGFW_UNUSED(a) RGFW_UNUSED(image) }
+
+const char RGFW_CURSORS[11][12] = {
+    "default",
+    "default",
+    "text",
+    "crosshair",
+    "pointer",
+    "ew-resize",
+    "ns-resize",
+    "nwse-resize",
+    "nesw-resize",
+    "move",
+    "not-allowed"
+};
+
+void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
+	RGFW_UNUSED(win)
+	EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, RGFW_CURSORS[mouse]);
+}
+
+void RGFW_window_setMouseDefault(RGFW_window* win) {
+	RGFW_window_setMouseStandard(win, RGFW_MOUSE_NORMAL);
+}
+
+void RGFW_window_showMouse(RGFW_window* win, i8 show) {
+	if (show)
+		RGFW_window_setMouseDefault(win);
+	else
+		EM_ASM(document.getElementById('canvas').style.cursor = 'none';);
+}
+
+RGFW_point RGFW_getGlobalMousePoint(void) {
+    RGFW_point point;
+    point.x = EM_ASM_INT({
+        return window.mouseX || 0;
+    });
+    point.y = EM_ASM_INT({
+        return window.mouseY || 0;
+    });
+    return point;
+}
+
+RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
+	RGFW_UNUSED(win);
+	
+	EmscriptenMouseEvent mouseEvent;
+    emscripten_get_mouse_status(&mouseEvent);
+	return RGFW_POINT( mouseEvent.targetX,  mouseEvent.targetY);
+}
+
+void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
+	RGFW_UNUSED(win);
+
+    EM_ASM_({
+        var canvas = document.getElementById('canvas');
+        if ($0) {
+            canvas.style.pointerEvents = 'none';
+        } else {
+            canvas.style.pointerEvents = 'auto';
+        }
+    }, passthrough);
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+	RGFW_UNUSED(textLen)
+	EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
+}
+
+
+char* RGFW_readClipboard(size_t* size) {
+	/*
+		placeholder code for later
+		I'm not sure if this is possible do the the async stuff
+	*/
+	
+	if (size != NULL)
+		*size = 0;
+	
+	char* str = (char*)malloc(1);
+	str[0] = '\0';
+
+	return str;
+}
+
+void RGFW_window_swapBuffers(RGFW_window* win) {
+	RGFW_UNUSED(win);
+	
+	#ifdef RGFW_BUFFER
+	if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) {		
+		glEnable(GL_TEXTURE_2D);
+
+		GLuint texture;
+		glGenTextures(1,&texture);
+
+		glBindTexture(GL_TEXTURE_2D,texture);
+		
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, RGFW_bufferSize.w, RGFW_bufferSize.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, win->buffer);
+		
+		float ratioX = ((float)win->r.w / (float)RGFW_bufferSize.w);
+		float ratioY = ((float)win->r.h / (float)RGFW_bufferSize.h);
+
+		// Set up the viewport
+		glClear(GL_COLOR_BUFFER_BIT);
+
+		glBegin(GL_TRIANGLES);
+			glTexCoord2f(0, ratioY); glColor3f(1, 1, 1); glVertex2f(-1, -1);
+			glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1);
+			glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1);
+
+			glTexCoord2f(ratioX, 0); glColor3f(1, 1, 1); glVertex2f(1, 1);
+			glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1);
+			glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1);
+		glEnd();
+
+		glDeleteTextures(1, &texture);
+	}
+	#endif
+
+	emscripten_webgl_commit_frame();
+	emscripten_sleep(0);
+}
+
+
+void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+	if (win == NULL)
+	    emscripten_webgl_make_context_current(0);
+	else
+	    emscripten_webgl_make_context_current(win->src.ctx);
+}
+
+#ifndef RGFW_EGL
+void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); }
+#endif
+
+void RGFW_window_close(RGFW_window* win) {
+    emscripten_webgl_destroy_context(win->src.ctx);
+
+    free(win);
+}
+
+int RGFW_innerWidth(void) {   return EM_ASM_INT({ return window.innerWidth; });  }
+int RGFW_innerHeight(void) {  return EM_ASM_INT({ return window.innerHeight; });  }
+
+RGFW_area RGFW_getScreenSize(void) {
+	return RGFW_AREA(RGFW_innerWidth(), RGFW_innerHeight());
+}
+
+void* RGFW_getProcAddress(const char* procname) { 
+	return emscripten_webgl_get_proc_address(procname);
+}
+
+void RGFW_sleep(u64 milisecond) {
+	emscripten_sleep(milisecond);
+}
+
+u64 RGFW_getTimeNS(void) {
+	return emscripten_get_now() * 1e+6;
+}
+
+u64 RGFW_getTime(void) {
+	return emscripten_get_now() * 1000;
+}
+
+void RGFW_releaseCursor(RGFW_window* win) {
+	RGFW_UNUSED(win);
+	emscripten_exit_pointerlock();
+}
+
+void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { 
+	RGFW_UNUSED(win); RGFW_UNUSED(r);
+
+	emscripten_request_pointerlock("#canvas", 1);
+}
+
+
+void RGFW_window_setName(RGFW_window* win, char* name) {
+	RGFW_UNUSED(win);
+	emscripten_set_window_title(name);
+}
+
+/* unsupported functions */
+RGFW_monitor* RGFW_getMonitors(void) { return NULL; }
+RGFW_monitor RGFW_getPrimaryMonitor(void) { return (RGFW_monitor){}; }
+void RGFW_window_move(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win) RGFW_UNUSED(v) }
+void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win) RGFW_UNUSED(a)  }
+void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win) RGFW_UNUSED(a)  }
+void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win)}
+void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win) }
+void RGFW_window_setBorder(RGFW_window* win, b8 border) { RGFW_UNUSED(win) RGFW_UNUSED(border)  }
+void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { RGFW_UNUSED(win) RGFW_UNUSED(icon) RGFW_UNUSED(a) RGFW_UNUSED(channels)  }
+void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win) }
+void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win) }
+b8 RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win) return 0; }
+b8 RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win) return 0; }
+b8 RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win) return 0; }
+RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win) return (RGFW_monitor){}; }
+
+#endif
+
+/* end of web asm defines */
+
+/* unix (macOS, linux, web asm) only stuff */
+#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM)  || defined(RGFW_WAYLAND)
+/* unix threading */
+#ifndef RGFW_NO_THREADS
+#include <pthread.h>
+
+	RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) {
+		RGFW_UNUSED(args);
+		
+		RGFW_thread t;
+		pthread_create((pthread_t*) &t, NULL, *ptr, NULL);
+		return t;
+	}
+	void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); }
+	void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); }
+#ifdef __linux__
+	void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio((pthread_t)thread, priority); }
+#endif
+#endif
+
+#ifndef RGFW_WEBASM
+/* unix sleep */
+	void RGFW_sleep(u64 ms) {
+		struct timespec time;
+		time.tv_sec = 0;
+		time.tv_nsec = ms * 1e+6;
+
+		nanosleep(&time, NULL);
+	}
+#endif
+
+#endif /* end of unix / mac stuff*/
+#endif /*RGFW_IMPLEMENTATION*/
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
 }
 #endif
diff --git a/raylib/src/external/glfw/src/mappings.h b/raylib/src/external/glfw/src/mappings.h
--- a/raylib/src/external/glfw/src/mappings.h
+++ b/raylib/src/external/glfw/src/mappings.h
@@ -996,6 +996,7 @@
 "03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,",
 "03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
 "03000000120c0000101e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux,",
 #endif // GLFW_BUILD_LINUX_JOYSTICK
 };
 
diff --git a/raylib/src/external/jar_xm.h b/raylib/src/external/jar_xm.h
--- a/raylib/src/external/jar_xm.h
+++ b/raylib/src/external/jar_xm.h
@@ -1204,7 +1204,7 @@
 }
 
 static void jar_xm_pitch_slide(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, float period_offset) {
-    /* Don't ask about the 4.f coefficient. I found mention of it nowhere. Found by ear™. */
+    /* Don't ask about the 4.f coefficient. I found mention of it nowhere. Found by ear. */
     if(ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES) {period_offset *= 4.f; }
     ch->period += period_offset;
     jar_xm_CLAMP_DOWN(ch->period);
@@ -1507,7 +1507,7 @@
             jar_xm_volume_slide(ch, ch->fine_volume_slide_param);
             break;
         case 0xD: /* EDy: Note delay */
-            /* XXX: figure this out better. EDx triggers the note even when there no note and no instrument. But ED0 acts like like a ghost note, EDx (x ≠ 0) does not. */
+            /* XXX: figure this out better. EDx triggers the note even when there no note and no instrument. But ED0 acts like like a ghost note, EDx (x != 0) does not. */
             if(s->note == 0 && s->instrument == 0) {
                 unsigned int flags = jar_xm_TRIGGER_KEEP_VOLUME;
                 if(ch->current->effect_param & 0x0F) {
@@ -1795,7 +1795,7 @@
             if(ch->current->effect_param > 0) {
                 char arp_offset = ctx->tempo % 3;
                 switch(arp_offset) {
-                case 2: /* 0 -> x -> 0 -> y -> x -> … */
+                case 2: /* 0 -> x -> 0 -> y -> x -> ... */
                     if(ctx->current_tick == 1) {
                         ch->arp_in_progress = true;
                         ch->arp_note_offset = ch->current->effect_param >> 4;
@@ -1803,7 +1803,7 @@
                         break;
                     }
                     /* No break here, this is intended */
-                case 1: /* 0 -> 0 -> y -> x -> … */
+                case 1: /* 0 -> 0 -> y -> x -> ... */
                     if(ctx->current_tick == 0) {
                         ch->arp_in_progress = false;
                         ch->arp_note_offset = 0;
@@ -1811,7 +1811,7 @@
                         break;
                     }
                     /* No break here, this is intended */
-                case 0: /* 0 -> y -> x -> … */
+                case 0: /* 0 -> y -> x -> ... */
                     jar_xm_arpeggio(ctx, ch, ch->current->effect_param, ctx->current_tick - arp_offset);
                 default:
                     break;
diff --git a/raylib/src/external/miniaudio.h b/raylib/src/external/miniaudio.h
# file too large to diff: raylib/src/external/miniaudio.h
diff --git a/raylib/src/external/nanosvg.h b/raylib/src/external/nanosvg.h
deleted file mode 100644
--- a/raylib/src/external/nanosvg.h
+++ /dev/null
@@ -1,3053 +0,0 @@
-/*
- * 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 SVG parser is based on Anti-Grain Geometry 2.4 SVG example
- * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
- *
- * Arc calculation code based on canvg (https://code.google.com/p/canvg/)
- *
- * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
- *
- */
-
-#ifndef NANOSVG_H
-#define NANOSVG_H
-
-#ifndef NANOSVG_CPLUSPLUS
-#ifdef __cplusplus
-extern "C" {
-#endif
-#endif
-
-// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
-//
-// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
-//
-// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
-//
-// The shapes in the SVG images are transformed by the viewBox and converted to specified units.
-// That is, you should get the same looking data as your designed in your favorite app.
-//
-// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
-// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
-//
-// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
-// DPI (dots-per-inch) controls how the unit conversion is done.
-//
-// If you don't know or care about the units stuff, "px" and 96 should get you going.
-
-
-/* Example Usage:
-	// Load SVG
-	NSVGimage* image;
-	image = nsvgParseFromFile("test.svg", "px", 96);
-	printf("size: %f x %f\n", image->width, 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 <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <math.h>
-
-#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/raylib/src/external/nanosvgrast.h b/raylib/src/external/nanosvgrast.h
deleted file mode 100644
--- a/raylib/src/external/nanosvgrast.h
+++ /dev/null
@@ -1,1458 +0,0 @@
-/*
- * 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 <math.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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, "<svg viewBox=\"%f %f %f %f\" xmlns=\"http://www.w3.org/2000/svg\">", xmin, ymin, (xmax - xmin), (ymax - ymin));
-
-	for (i = 0; i < r->nedges; i++) {
-		e = &r->edges[i];
-		fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#000;\" />", e->x0,e->y0, e->x1,e->y1);
-	}
-
-	for (i = 0; i < r->npoints; i++) {
-		if (i+1 < r->npoints)
-			fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#f00;\" />", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y);
-		fprintf(fp ,"<circle cx=\"%f\" cy=\"%f\" r=\"1\" style=\"fill:%s;\" />", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0");
-	}
-
-	fprintf(fp, "</svg>");
-	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/raylib/src/external/par_shapes.h b/raylib/src/external/par_shapes.h
--- a/raylib/src/external/par_shapes.h
+++ b/raylib/src/external/par_shapes.h
@@ -1130,7 +1130,7 @@
             total += rule->weight;
         }
     }
-    float r = (float) rand() / RAND_MAX;
+    float r = (float) rand() / (float) RAND_MAX;
     float t = 0;
     for (int i = 0; i < nrules; i++) {
         rule = rules + i;
diff --git a/raylib/src/external/stb_image_resize2.h b/raylib/src/external/stb_image_resize2.h
--- a/raylib/src/external/stb_image_resize2.h
+++ b/raylib/src/external/stb_image_resize2.h
@@ -1,10303 +1,10572 @@
-/* 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 <stddef.h>
-#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 <stdint.h>
-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 <assert.h>
-#define STBIR_ASSERT(x) assert(x)
-#endif
-
-#ifndef STBIR_MALLOC
-#include <stdlib.h>
-#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 <emmintrin.h>
-  
-  #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 <immintrin.h>
-  #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 <smmintrin.h>
-    #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 <immintrin.h>
-    #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 <arm_neon.h>
-
-  #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 <wasm_simd128.h>
-
-  #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 <immintrin.h>
-
-  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;i<STBIR__ARRAY_SIZE(info->profile.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;i<STBIR__ARRAY_SIZE((info)->profile.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;i<STBIR__ARRAY_SIZE(info->profile.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 <math.h>
-#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 <string.h>
-#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 channels<effective_channels, we are right justified in the buffer)
-    stbir_info->decode_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 
+/* stb_image_resize2 - v2.10 - 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
+      Nathan Reed: warning fixes for 1.0
+
+   REVISIONS
+      2.10 (2024-07-27) fix the defines GCC and mingw for loop unroll control,
+                          fix MSVC 32-bit arm half float routines.
+      2.09 (2024-06-19) fix the defines for 32-bit ARM GCC builds (was selecting
+                          hardware half floats).
+      2.08 (2024-06-10) fix for RGB->BGR three channel flips and add SIMD (thanks
+                          to Ryan Salsbury), fix for sub-rect resizes, use the
+                          pragmas to control unrolling when they are available.
+      2.07 (2024-05-24) fix for slow final split during threaded conversions of very 
+                          wide scanlines when downsampling (caused by extra input 
+                          converting), fix for wide scanline resamples with many 
+                          splits (int overflow), fix GCC warning.
+      2.06 (2024-02-10) fix for identical width/height 3x or more down-scaling 
+                          undersampling a single row on rare resize ratios (about 1%).
+      2.05 (2024-02-07) fix for 2 pixel to 1 pixel resizes with wrap (thanks Aras),
+                        fix for output callback (thanks Julien Koenen).
+      2.04 (2023-11-17) fix for rare AVX bug, shadowed symbol (thanks Nikola Smiljanic).
+      2.03 (2023-11-01) ASAN and TSAN warnings fixed, minor tweaks.
+      2.00 (2023-10-10) 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 esp resizing large to very small.
+      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 <stddef.h>
+#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 <stdint.h>
+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__ ) || ((__ARM_NEON_FP & 4) != 0) || defined(__ARM_NEON__)
+#ifndef STBIR_NEON
+#define STBIR_NEON
+#endif
+#endif
+
+#if defined(_M_ARM) || defined(__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_1CHANNEL = 1,
+  STBIR_2CHANNEL = 2,
+  STBIR_RGB      = 3,               // 3-chan, with order specified (for channel flipping)
+  STBIR_BGR      = 0,               // 3-chan, with order specified (for channel flipping)
+  STBIR_4CHANNEL = 5,
+
+  STBIR_RGBA = 4,                   // alpha formats, where alpha is NOT premultiplied into color channels
+  STBIR_BGRA = 6,
+  STBIR_ARGB = 7,
+  STBIR_ABGR = 8,
+  STBIR_RA   = 9,
+  STBIR_AR   = 10,
+
+  STBIR_RGBA_PM = 11,               // alpha formats, where 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_RGBA_NO_AW = 11,            // alpha formats, where NO alpha weighting is applied at all!
+  STBIR_BGRA_NO_AW = 12,            //   these are just synonyms for the _PM flags (which also do
+  STBIR_ARGB_NO_AW = 13,            //   no alpha weighting). These names just make it more clear
+  STBIR_ABGR_NO_AW = 14,            //   for some folks).
+  STBIR_RA_NO_AW   = 15,
+  STBIR_AR_NO_AW   = 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 <assert.h>
+#define STBIR_ASSERT(x) assert(x)
+#endif
+
+#ifndef STBIR_MALLOC
+#include <stdlib.h>
+#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 alloc_ring_buffer_num_entries;    // Number of entries in the ring buffer that will be allocated
+  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)
+  size_t alloced_total;
+};
+
+
+#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) for(;;) { \
+  if ( (x) < (xmin) ) (x) = (xmin);     \
+  if ( (x) > (xmax) ) (x) = (xmax);     \
+  break;                                \
+}
+
+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
+
+#ifndef STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS
+#define STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS 4 // when threading, what is the minimum number of scanlines for a split?
+#endif
+
+// restrict pointers for the output pointers, other loop and unroll control
+#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
+  #if _MSC_VER >= 1900
+    #define STBIR_NO_UNROLL_LOOP_START __pragma(loop( no_vector )) 
+  #else
+    #define STBIR_NO_UNROLL_LOOP_START 
+  #endif
+#elif defined( __clang__ )
+  #define STBIR_STREAMOUT_PTR( star ) star __restrict__
+  #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr)) 
+  #if ( __clang_major__ >= 4 ) || ( ( __clang_major__ >= 3 ) && ( __clang_minor__ >= 5 ) )
+    #define STBIR_NO_UNROLL_LOOP_START _Pragma("clang loop unroll(disable)") _Pragma("clang loop vectorize(disable)")
+  #else
+    #define STBIR_NO_UNROLL_LOOP_START
+  #endif 
+#elif defined( __GNUC__ )
+  #define STBIR_STREAMOUT_PTR( star ) star __restrict__
+  #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr))
+  #if __GNUC__ >= 14
+    #define STBIR_NO_UNROLL_LOOP_START _Pragma("GCC unroll 0") _Pragma("GCC novector")
+  #else
+    #define STBIR_NO_UNROLL_LOOP_START
+  #endif
+  #define STBIR_NO_UNROLL_LOOP_START_INF_FOR
+#else
+  #define STBIR_STREAMOUT_PTR( star ) star
+  #define STBIR_NO_UNROLL( ptr )
+  #define STBIR_NO_UNROLL_LOOP_START
+#endif
+
+#ifndef STBIR_NO_UNROLL_LOOP_START_INF_FOR
+#define STBIR_NO_UNROLL_LOOP_START_INF_FOR STBIR_NO_UNROLL_LOOP_START
+#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 <emmintrin.h>
+
+  #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 <immintrin.h>
+  #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 <smmintrin.h>
+    #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 <immintrin.h>
+    #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_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_setr_m128( mul, _mm_setzero_ps() ), _mm256_setr_m128( _mm_loadu_ps( (float const*)(ptr) ), _mm_setzero_ps() ), 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_setr_m128( _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ), _mm_setzero_ps() ) )
+    #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 <arm_neon.h>
+
+  #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) ) )
+
+      static stbir__inline uint8x16x2_t stbir_make16x2(float32x4_t rega,float32x4_t regb)
+      {
+        uint8x16x2_t r = { vreinterpretq_u8_f32(rega), vreinterpretq_u8_f32(regb) };
+        return r;
+      }
+    #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}
+      #define stbir_make16x2(a,b) (uint8x16x2_t){{vreinterpretq_u8_f32(a),vreinterpretq_u8_f32(b)}}
+    #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__simdf_swiz2( rega, regb, one, two, three, four ) vreinterpretq_f32_u8( vqtbl2q_u8( stbir_make16x2(rega,regb), 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 <wasm_simd128.h>
+
+  #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) && !defined(__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)) || (defined(STBIR_NEON) && defined(__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 <immintrin.h>
+
+  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_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) && ( defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) ) // 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);
+  }
+
+#elif defined(STBIR_WASM) || (defined(STBIR_NEON) && (defined(_MSC_VER) || defined(_M_ARM) || defined(__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]);
+    }
+  }
+
+#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)
+#define STBIR_SIMD_NO_UNROLL_LOOP_START STBIR_NO_UNROLL_LOOP_START
+#define STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR STBIR_NO_UNROLL_LOOP_START_INF_FOR
+
+#ifdef STBIR_MEMCPY
+#undef STBIR_MEMCPY
+#endif
+#define STBIR_MEMCPY stbir_simd_memcpy
+
+// 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 )
+      {
+        STBIR_SIMD_NO_UNROLL_LOOP_START
+        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*)( ( ( (size_t)d ) + 16 ) & ~15 );
+
+      STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+      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*)( ( ( (size_t)d ) + (16*stbir__simdfX_float_count) ) & ~((16*stbir__simdfX_float_count)-1) );
+
+    STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+    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);
+    STBIR_SIMD_NO_UNROLL_LOOP_START
+    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)
+#define STBIR_SIMD_NO_UNROLL_LOOP_START
+#define STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+
+#endif // SSE2
+
+
+#ifdef STBIR_PROFILE
+
+#ifndef STBIR_PROFILE_FUNC
+
+#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, arm
+
+#endif // STBIR_PROFILE_FUNC
+
+#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;i<STBIR__ARRAY_SIZE(info->profile.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;i<STBIR__ARRAY_SIZE((info)->profile.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;i<STBIR__ARRAY_SIZE(info->profile.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 <math.h>
+#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 <string.h>
+#define STBIR_MEMCPY( dest, src, len ) memcpy( dest, src, len )
+#endif
+
+#ifndef STBIR_SIMD
+
+// memcpy that is specifically 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);
+    STBIR_NO_UNROLL_LOOP_START
+    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;
+  }
+
+  STBIR_NO_UNROLL_LOOP_START
+  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_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;
+    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;
+        }
+      }
+    }
+  }
+}
+
+#ifdef STBIR_RENORMALIZE_IN_FLOAT
+#define STBIR_RENORM_TYPE float
+#else
+#define STBIR_RENORM_TYPE double
+#endif
+
+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;
+    STBIR_RENORM_TYPE filter_scale, total_filter = 0;
+    int e;
+
+    // add all contribs
+    e = contribs->n1 - contribs->n0;
+    for( i = 0 ; i <= e ; i++ )
+    {
+      total_filter += (STBIR_RENORM_TYPE) 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 = ((STBIR_RENORM_TYPE)1.0) / total_filter;
+
+        // scale them all
+        for (i = 0; i <= e; i++)
+          coeffs[i] = (float) ( 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;
+}
+
+#undef STBIR_RENORM_TYPE 
+
+static int stbir__pack_coefficients( int num_contributors, stbir__contributors* contributors, float * coefficents, int coefficient_width, int widest, int row0, int row1 ) 
+{
+  #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
+
+  int row_end = row1 + 1;
+  STBIR__UNUSED( row0 ); // only used in an assert
+
+  if ( coefficient_width != widest )
+  {
+    float * pc = coefficents;
+    float * coeffs = coefficents;
+    float * pc_end = coefficents + num_contributors * widest;
+    switch( widest )
+    {
+      case 1:
+        STBIR_NO_UNROLL_LOOP_START
+        do {
+          STBIR_MOVE_1( pc, coeffs );
+          ++pc;
+          coeffs += coefficient_width;
+        } while ( pc < pc_end );
+        break;
+      case 2:
+        STBIR_NO_UNROLL_LOOP_START
+        do {
+          STBIR_MOVE_2( pc, coeffs );
+          pc += 2;
+          coeffs += coefficient_width;
+        } while ( pc < pc_end );
+        break;
+      case 3:
+        STBIR_NO_UNROLL_LOOP_START
+        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:
+        STBIR_NO_UNROLL_LOOP_START
+        do {
+          STBIR_MOVE_4( pc, coeffs );
+          pc += 4;
+          coeffs += coefficient_width;
+        } while ( pc < pc_end );
+        break;
+      case 5:
+        STBIR_NO_UNROLL_LOOP_START
+        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:
+        STBIR_NO_UNROLL_LOOP_START
+        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:
+        STBIR_NO_UNROLL_LOOP_START
+        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:
+        STBIR_NO_UNROLL_LOOP_START
+        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:
+        STBIR_NO_UNROLL_LOOP_START
+        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:
+        STBIR_NO_UNROLL_LOOP_START
+        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:
+        STBIR_NO_UNROLL_LOOP_START
+        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:
+        STBIR_NO_UNROLL_LOOP_START
+        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:
+        STBIR_NO_UNROLL_LOOP_START
+        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;
+          STBIR_NO_UNROLL_LOOP_START
+          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 >= contributors ) && ( ( contribs->n0 + widest*2 ) >= row_end ) )
+    {
+      // might we clip??
+      if ( ( contribs->n0 + widest ) > row_end )
+      {
+        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_end )
+        {
+          int new_n0 = row_end - 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 >= row0 ) && ( 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++;
+            
+            // skip zero and denormals - must skip zeros to avoid adding coeffs beyond scatter_coefficient_width
+            //   (which happens when pivoting from horizontal, which might have dummy zeros)
+            if ( ( ( gc >= stbir__small_float ) || ( gc <= -stbir__small_float ) ) )
+            {
+              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 );
+              }
+              STBIR_ASSERT( ( scatter_contributors->n1 - scatter_contributors->n0 + 1 ) <= scatter_coefficient_width );
+            }
+            ++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;
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+  STBIR_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  while ( decode < end_decode )
+  #else
+  if ( decode < end_decode )
+  #endif
+  {
+    stbir__simdf d,a,p;
+    STBIR_NO_UNROLL(decode);
+    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 )
+  {
+    STBIR_NO_UNROLL_LOOP_START
+    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
+
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+    STBIR_NO_UNROLL_LOOP_START
+    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
+    STBIR_NO_UNROLL_LOOP_START
+    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;
+  STBIR_NO_UNROLL_LOOP_START
+  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
+
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+
+#ifdef STBIR_SIMD
+    #ifdef stbir__simdf_swiz2 // do we have two argument swizzles?
+      end_decode -= 12; 
+      STBIR_NO_UNROLL_LOOP_START
+      while( decode <= end_decode )
+      {
+        // on arm64 8 instructions, no overlapping stores
+        stbir__simdf a,b,c,na,nb;
+        STBIR_SIMD_NO_UNROLL(decode);
+        stbir__simdf_load( a, decode );
+        stbir__simdf_load( b, decode+4 );
+        stbir__simdf_load( c, decode+8 );
+
+        na = stbir__simdf_swiz2( a, b, 2, 1, 0, 5 );   
+        b  = stbir__simdf_swiz2( a, b, 4, 3, 6, 7 );   
+        nb = stbir__simdf_swiz2( b, c, 0, 1, 4, 3 );   
+        c  = stbir__simdf_swiz2( b, c, 2, 7, 6, 5 );   
+
+        stbir__simdf_store( decode, na );
+        stbir__simdf_store( decode+4, nb ); 
+        stbir__simdf_store( decode+8, c );
+        decode += 12;
+      }
+      end_decode += 12;
+    #else
+      end_decode -= 24;
+      STBIR_NO_UNROLL_LOOP_START
+      while( decode <= end_decode )
+      {
+        // 26 instructions on x64
+        stbir__simdf a,b,c,d,e,f,g;
+        float i21, i23;
+        STBIR_SIMD_NO_UNROLL(decode);
+        stbir__simdf_load( a, decode );
+        stbir__simdf_load( b, decode+3 );
+        stbir__simdf_load( c, decode+6 );
+        stbir__simdf_load( d, decode+9 );
+        stbir__simdf_load( e, decode+12 );
+        stbir__simdf_load( f, decode+15 );
+        stbir__simdf_load( g, decode+18 );
+
+        a = stbir__simdf_swiz( a, 2, 1, 0, 3 );   
+        b = stbir__simdf_swiz( b, 2, 1, 0, 3 );   
+        c = stbir__simdf_swiz( c, 2, 1, 0, 3 );   
+        d = stbir__simdf_swiz( d, 2, 1, 0, 3 );   
+        e = stbir__simdf_swiz( e, 2, 1, 0, 3 );   
+        f = stbir__simdf_swiz( f, 2, 1, 0, 3 );   
+        g = stbir__simdf_swiz( g, 2, 1, 0, 3 );   
+
+        // stores overlap, need to be in order, 
+        stbir__simdf_store( decode,    a );
+        i21 = decode[21];
+        stbir__simdf_store( decode+3,  b ); 
+        i23 = decode[23];
+        stbir__simdf_store( decode+6,  c );
+        stbir__simdf_store( decode+9,  d );
+        stbir__simdf_store( decode+12, e );
+        stbir__simdf_store( decode+15, f );
+        stbir__simdf_store( decode+18, g );
+        decode[21] = i23;
+        decode[23] = i21;
+        decode += 24;
+      }
+      end_decode += 24;
+    #endif
+#else
+  end_decode -= 12;
+  STBIR_NO_UNROLL_LOOP_START
+  while( decode <= end_decode )
+  {
+    // 16 instructions
+    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;
+  }
+  end_decode += 12;
+#endif
+
+  STBIR_NO_UNROLL_LOOP_START
+  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 ) + (size_t)row * (size_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 channels<effective_channels, we are right justified in the buffer)
+    stbir_info->decode_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,d;                           \
+    stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 );    \
+    stbir__simdf_0123to2301( d, t );              \
+    stbir__simdf_add( t, t, d );                  \
+    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, 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 ) + ((size_t)n * (size_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 = vertical_contributors->n0;
+  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 ) + ( (size_t)split_info->ring_buffer_first_scanline * (size_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 ) + ( (size_t)split_info->ring_buffer_first_scanline * (size_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);
+
+  // filter_pixel_width is the conservative size in pixels of input that affect an output pixel.
+  //   In rare cases (only with 2 pix to 1 pix with the default filters), it's possible that the 
+  //   filter will extend before or after the scanline beyond just one extra entire copy of the 
+  //   scanline (we would hit the edge twice). We don't let you do that, so we clamp the total 
+  //   width to 3x the total of input pixel (once for the scanline, once for the left side 
+  //   overhang, and once for the right side). We only do this for edge mode, since the other 
+  //   modes can just re-edge clamp back in again.
+  if ( edge == STBIR_EDGE_WRAP )
+    if ( samp->filter_pixel_width > ( scale_info->input_full_size * 3 ) )
+      samp->filter_pixel_width = scale_info->input_full_size * 3;
+
+  // 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;
+  
+  // filter_pixel_margin is the amount that this filter can overhang on just one side of either 
+  //   end of the scanline (left or the right). Since we only allow you to overhang 1 scanline's 
+  //   worth of pixels, we clamp this one side of overhang to the input scanline size. Again, 
+  //   this clamping only happens in rare cases with the default filters (2 pix to 1 pix). 
+  if ( edge == STBIR_EDGE_WRAP )
+    if ( samp->filter_pixel_margin > scale_info->input_full_size )
+      samp->filter_pixel_margin = scale_info->input_full_size;
+
+  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;
+  size_t 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.n0, info->scanline_extents.conservative.n1 );
+
+      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 past the end of the decode (but mask it out), 
+      //   so put in normal values so no snans or denormals accidentally sneak in (also, in the ring 
+      //   buffer for vertical first)
+      for( i = 0 ; i < splits ; i++ )
+      {
+        int t, ofs, start;
+
+        ofs = decode_buffer_size / 4;
+        start = ofs - 4;
+        if ( start < 0 ) start = 0;
+
+        for( t = start ; t < ofs; t++ )
+          info->split_info[i].decode_buffer[ t ] = 9999.0f;
+
+        if ( vertical_first )
+        {
+          int j;
+          for( j = 0; j < info->ring_buffer_num_entries ; j++ )
+          {
+            for( t = start ; t < ofs; t++ )
+              stbir__get_ring_buffer_entry( info, info->split_info + i, j )[ t ] = 9999.0f;
+          }
+        }
+      }
+    }
+
+    #undef STBIR__NEXT_PTR
+
+
+    // is this the first time through loop?
+    if ( info == 0 )
+    {
+      alloced_total = ( 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 ) + ( (size_t) info->offset_y * (size_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->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;
+  resize->needs_rebuild = 1;
+}
+
+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;
+  if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
+    stbir__update_info_from_resize( resize->samplers, resize );
+}
+
+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;
+
+  if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
+  {
+    resize->samplers->in_pixels_cb = input_cb;
+    resize->samplers->out_pixels_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;
+  if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
+    resize->samplers->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;
+  if ( ( resize->samplers ) && ( !resize->needs_rebuild ) )
+    stbir__update_info_from_resize( resize->samplers, resize );
+}
+
+
+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 ) < STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS ) // each split should be a minimum of 4 scanlines (handwavey choice)
+  {
+    splits = vertical.scale_info.output_sub_size / STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS;
+    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
+
+    // update anything that can be changed without recalcing samplers
+    stbir__update_info_from_resize( out_info, resize );
+
+    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 );
+  }
+
+  // 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;
+
+  // 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 = (size_t)output_stride_in_bytes * (size_t)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;
+    STBIR_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+    STBIR_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+    STBIR_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+    STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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
+
+  if ( width_times_channels >= 16 )
+  {
+    float const * end_encode_m16 = encode + width_times_channels - 16;
+    end_output -= 16;
+    STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+    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, ( fp32_to_srgb8_tab4 - (127-13)*8 ) );
+
+      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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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
+
+  if ( width_times_channels >= 16 )
+  {
+    float const * end_encode_m16 = encode + width_times_channels - 16;
+    end_output -= 16;
+    STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+    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, ( fp32_to_srgb8_tab4 - (127-13)*8 ) );
+
+      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
+
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+
+  if ( width_times_channels >= 16 )
+  {
+    float const * end_encode_m16 = encode + width_times_channels - 16;
+    end_output -= 16;
+    STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+    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, ( fp32_to_srgb8_tab4 - (127-13)*8 ) );
+
+      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
+
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+    STBIR_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+      STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+      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;
+  STBIR_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+    STBIR_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+      STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+      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;
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+    STBIR_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+    STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+    STBIR_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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 );
+    STBIR_SIMD_NO_UNROLL_LOOP_START_INF_FOR
+    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;
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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 ); )
+    STBIR_SIMD_NO_UNROLL_LOOP_START
+    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); )
+    }
+    STBIR_SIMD_NO_UNROLL_LOOP_START
+    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
+  STBIR_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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 ); )
+
+    STBIR_SIMD_NO_UNROLL_LOOP_START
+    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); )
+    }
+
+    STBIR_SIMD_NO_UNROLL_LOOP_START
+    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
+  STBIR_NO_UNROLL_LOOP_START
+  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
+  STBIR_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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();
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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();
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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();
+    STBIR_SIMD_NO_UNROLL_LOOP_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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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();
+    STBIR_SIMD_NO_UNROLL_LOOP_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;
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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();
+    STBIR_SIMD_NO_UNROLL_LOOP_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();
+  STBIR_SIMD_NO_UNROLL_LOOP_START
+  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();
+    STBIR_SIMD_NO_UNROLL_LOOP_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/raylib/src/platforms/rcore_android.c b/raylib/src/platforms/rcore_android.c
--- a/raylib/src/platforms/rcore_android.c
+++ b/raylib/src/platforms/rcore_android.c
@@ -74,7 +74,7 @@
 // Global Variables Definition
 //----------------------------------------------------------------------------------
 extern CoreData CORE;                   // Global CORE state context
-
+extern bool isGpuReady;                 // Flag to note GPU has been initialized successfully
 static PlatformData platform = { 0 };   // Platform specific data
 
 //----------------------------------------------------------------------------------
@@ -281,14 +281,15 @@
     // Request to end the native activity
     ANativeActivity_finish(app->activity);
 
-    // Android ALooper_pollAll() variables
+    // Android ALooper_pollOnce() 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 **)&platform.source)) >= 0)
+        // Poll all events until we reach return value TIMEOUT, meaning no events left to process
+        while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void **)&platform.source)) > ALOOPER_POLL_TIMEOUT)
         {
             if (platform.source != NULL) platform.source->process(app, platform.source);
         }
@@ -632,6 +633,13 @@
     TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
 }
 
+// Get physical key name.
+const char *GetKeyName(int key)
+{
+    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
+    return "";
+}
+
 // Register all input events
 void PollInputEvents(void)
 {
@@ -675,27 +683,27 @@
         CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
     }
 
-    // Android ALooper_pollAll() variables
+    // Android ALooper_pollOnce() variables
     int pollResult = 0;
     int pollEvents = 0;
 
-    // Poll Events (registered events)
+    // Poll Events (registered events) until we reach TIMEOUT which indicates there are no events left to poll
     // NOTE: Activity is paused if not enabled (platform.appEnabled)
-    while ((pollResult = ALooper_pollAll(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) >= 0)
+    while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
     {
         // 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!
+        // NOTE: Allow closing the window in case a configuration change happened.
+        // The android_main function should be allowed to return to its caller in order for the
+        // Android OS to relaunch the activity.
         if (platform.app->destroyRequested != 0)
         {
-            //CORE.Window.shouldClose = true;
-            //ANativeActivity_finish(platform.app->activity);
+            CORE.Window.shouldClose = true;
         }
     }
 }
 
-
 //----------------------------------------------------------------------------------
 // Module Internal Functions Definition
 //----------------------------------------------------------------------------------
@@ -760,20 +768,20 @@
 
     TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Initialized successfully");
 
-    // Android ALooper_pollAll() variables
+    // Android ALooper_pollOnce() 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 events until we reach TIMEOUT, which indicates no more events queued.
+        while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
         {
             // 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!
+            // NOTE: It's highly likely destroyRequested will never be non-zero at the start of the activity lifecycle.
             //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true;
         }
     }
@@ -804,6 +812,12 @@
         eglTerminate(platform.device);
         platform.device = EGL_NO_DISPLAY;
     }
+
+    // NOTE: Reset global state in case the activity is being relaunched.
+    if (platform.app->destroyRequested != 0) {
+        CORE = (CoreData){0};
+        platform = (PlatformData){0};
+    }
 }
 
 // Initialize display device and framebuffer
@@ -974,6 +988,7 @@
                     // 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);
+                    isGpuReady = true;
 
                     // Setup default viewport
                     // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
diff --git a/raylib/src/platforms/rcore_desktop_glfw.c b/raylib/src/platforms/rcore_desktop_glfw.c
--- a/raylib/src/platforms/rcore_desktop_glfw.c
+++ b/raylib/src/platforms/rcore_desktop_glfw.c
@@ -109,6 +109,7 @@
 
 // Window callbacks events
 static void WindowSizeCallback(GLFWwindow *window, int width, int height);                 // GLFW3 WindowSize Callback, runs when window is resized
+static void WindowPosCallback(GLFWwindow* window, int x, int y);                     // GLFW3 WindowPos Callback, runs when window is moved
 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
@@ -147,8 +148,8 @@
     if (!CORE.Window.fullscreen)
     {
         // Store previous window position (in case we exit fullscreen)
-        glfwGetWindowPos(platform.handle, &CORE.Window.position.x, &CORE.Window.position.y);
-
+        CORE.Window.previousPosition = CORE.Window.position;
+        
         int monitorCount = 0;
         int monitorIndex = GetCurrentMonitor();
         GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
@@ -179,7 +180,11 @@
         CORE.Window.fullscreen = false;
         CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
 
-        glfwSetWindowMonitor(platform.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.previousPosition.x, CORE.Window.previousPosition.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
+
+        // we update the window position right away
+        CORE.Window.position.x = CORE.Window.previousPosition.x;
+        CORE.Window.position.y = CORE.Window.previousPosition.y;
     }
 
     // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
@@ -190,11 +195,11 @@
 // Toggle borderless windowed mode
 void ToggleBorderlessWindowed(void)
 {
-    // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it
+    // Leave fullscreen before attempting to set borderless windowed mode
     bool wasOnFullscreen = false;
     if (CORE.Window.fullscreen)
     {
-        CORE.Window.previousPosition = CORE.Window.position;
+        // fullscreen already saves the previous position so it does not need to be set here again
         ToggleFullscreen();
         wasOnFullscreen = true;
     }
@@ -213,7 +218,7 @@
             {
                 // Store screen position and size
                 // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here
-                if (!wasOnFullscreen) glfwGetWindowPos(platform.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y);
+                if (!wasOnFullscreen) CORE.Window.previousPosition = CORE.Window.position;
                 CORE.Window.previousScreen = CORE.Window.screen;
 
                 // Set undecorated and topmost modes and flags
@@ -255,6 +260,9 @@
                 glfwFocusWindow(platform.handle);
 
                 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
+
+                CORE.Window.position.x = CORE.Window.previousPosition.x;
+                CORE.Window.position.y = CORE.Window.previousPosition.y;
             }
         }
         else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
@@ -592,6 +600,9 @@
 // Set window position on screen (windowed mode)
 void SetWindowPosition(int x, int y)
 {
+    // Update CORE.Window.position as well
+    CORE.Window.position.x = x;
+    CORE.Window.position.y = y;
     glfwSetWindowPos(platform.handle, x, y);
 }
 
@@ -614,8 +625,9 @@
         {
             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;
+            // Here the render width has to be used again in case high dpi flag is enabled
+            const int screenWidth = CORE.Window.render.width;
+            const int screenHeight = CORE.Window.render.height;
             int monitorWorkareaX = 0;
             int monitorWorkareaY = 0;
             int monitorWorkareaWidth = 0;
@@ -1075,6 +1087,12 @@
     }
 }
 
+// Get physical key name.
+const char *GetKeyName(int key)
+{
+    return glfwGetKeyName(key, glfwGetKeyScancode(key));
+}
+
 // Register all input events
 void PollInputEvents(void)
 {
@@ -1216,7 +1234,6 @@
     glfwSetWindowShouldClose(platform.handle, GLFW_FALSE);
 }
 
-
 //----------------------------------------------------------------------------------
 // Module Internal Functions Definition
 //----------------------------------------------------------------------------------
@@ -1269,6 +1286,11 @@
     //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
 
+    // Disable GlFW auto iconify behaviour
+    // Auto Iconify automatically minimizes (iconifies) the window if the window loses focus
+    // additionally auto iconify restores the hardware resolution of the monitor if the window that loses focus is a fullscreen window
+    glfwWindowHint(GLFW_AUTO_ICONIFY, 0); 
+
     // Check window creation flags
     if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
 
@@ -1424,7 +1446,7 @@
             }
         }
 
-        TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
+        TRACELOG(LOG_INFO, "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),
@@ -1448,11 +1470,6 @@
         // No-fullscreen window creation
         bool requestWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0);
 
-        // If we are windowed fullscreen, ensures that window does not minimize when focus is lost.
-        // This hinting code will not work if the user already specified the correct monitor dimensions;
-        // at this point we don't know the monitor's dimensions. (Though, how did the user then?)
-        if (requestWindowedFullscreen) glfwWindowHint(GLFW_AUTO_ICONIFY, 0);
-
         // Default to at least one pixel in size, as creation with a zero dimension is not allowed.
         int creationWidth = CORE.Window.screen.width != 0 ? CORE.Window.screen.width : 1;
         int creationHeight = CORE.Window.screen.height != 0 ? CORE.Window.screen.height : 1;
@@ -1562,12 +1579,17 @@
         int monitorWidth = 0;
         int monitorHeight = 0;
         glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight);
-
-        int posX = monitorX + (monitorWidth - (int)CORE.Window.screen.width)/2;
-        int posY = monitorY + (monitorHeight - (int)CORE.Window.screen.height)/2;
+        
+        // Here CORE.Window.render.width/height should be used instead of CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled.
+        int posX = monitorX + (monitorWidth - (int)CORE.Window.render.width)/2;
+        int posY = monitorY + (monitorHeight - (int)CORE.Window.render.height)/2;
         if (posX < monitorX) posX = monitorX;
         if (posY < monitorY) posY = monitorY;
         SetWindowPosition(posX, posY);
+
+        // Update CORE.Window.position here so it is correct from the start
+        CORE.Window.position.x = posX;
+        CORE.Window.position.y = posY;
     }
 
     // Load OpenGL extensions
@@ -1579,6 +1601,7 @@
     //----------------------------------------------------------------------------
     // Set window callback events
     glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback);      // NOTE: Resizing not allowed by default!
+    glfwSetWindowPosCallback(platform.handle, WindowPosCallback);
     glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback);
     glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
     glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
@@ -1624,16 +1647,16 @@
     char *glfwPlatform = "";
     switch (glfwGetPlatform())
     {
-        case GLFW_PLATFORM_WIN32:   glfwPlatform = "Win32";   break;
-        case GLFW_PLATFORM_COCOA:   glfwPlatform = "Cocoa";   break;
+        case GLFW_PLATFORM_WIN32: glfwPlatform = "Win32"; break;
+        case GLFW_PLATFORM_COCOA: glfwPlatform = "Cocoa"; break;
         case GLFW_PLATFORM_WAYLAND: glfwPlatform = "Wayland"; break;
-        case GLFW_PLATFORM_X11:     glfwPlatform = "X11";     break;
-        case GLFW_PLATFORM_NULL:    glfwPlatform = "Null";    break;
+        case GLFW_PLATFORM_X11: glfwPlatform = "X11"; break;
+        case GLFW_PLATFORM_NULL: glfwPlatform = "Null"; break;
+        default: break;
     }
 #endif
 
-    TRACELOG(LOG_INFO, "GLFW platform: %s", glfwPlatform);
-    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully");
+    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW - %s): Initialized successfully", glfwPlatform);
 
     return 0;
 }
@@ -1675,7 +1698,12 @@
 
     // NOTE: Postprocessing texture is not scaled to new size
 }
-
+static void WindowPosCallback(GLFWwindow* window, int x, int y)
+{
+    // Set current window position
+    CORE.Window.position.x = x;
+    CORE.Window.position.y = y;
+}
 static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
 {
     CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
diff --git a/raylib/src/platforms/rcore_desktop_rgfw.c b/raylib/src/platforms/rcore_desktop_rgfw.c
--- a/raylib/src/platforms/rcore_desktop_rgfw.c
+++ b/raylib/src/platforms/rcore_desktop_rgfw.c
@@ -1,6 +1,6 @@
 /**********************************************************************************************
 *
-*   rcore_desktop_rgfw template - Functions to manage window, graphics device and inputs
+*   rcore_desktop_rgfw - Functions to manage window, graphics device and inputs
 *
 *   PLATFORM: RGFW
 *       - Windows (Win32, Win64)
@@ -46,7 +46,7 @@
 *
 **********************************************************************************************/
 
-#ifdef GRAPHICS_API_OPENGL_ES2
+#if defined(GRAPHICS_API_OPENGL_ES2)
     #define RGFW_OPENGL_ES2
 #endif
 
@@ -69,6 +69,7 @@
     #define CloseWindow CloseWindow_win32
     #define ShowCursor __imp_ShowCursor
     #define _APISETSTRING_
+    __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
 #endif
 
 #if defined(__APPLE__)
@@ -76,10 +77,6 @@
     #define Size NSSIZE
 #endif
 
-#if defined(_MSC_VER)
-__declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
-#endif
-
 #include "../external/RGFW.h"
 
 #if defined(__WIN32) || defined(__WIN64)
@@ -182,7 +179,7 @@
     [RGFW_u] = KEY_U,
     [RGFW_v] = KEY_V,
     [RGFW_w] = KEY_W,
-    [RGFW_x] KEY_X,
+    [RGFW_x] = KEY_X,
     [RGFW_y] = KEY_Y,
     [RGFW_z] = KEY_Z,
     [RGFW_Bracket] = KEY_LEFT_BRACKET,
@@ -291,7 +288,6 @@
     }
     if (flags & FLAG_WINDOW_RESIZABLE)
     {
-        printf("%i %i\n", platform.window->r.w, platform.window->r.h);
         RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
         RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
     }
@@ -483,14 +479,14 @@
 // Set title for window
 void SetWindowTitle(const char *title)
 {
-    RGFW_window_setName(platform.window, title);
+    RGFW_window_setName(platform.window, (char*)title);
     CORE.Window.title = title;
 }
 
 // Set window position on screen (windowed mode)
 void SetWindowPosition(int x, int y)
 {
-    RGFW_window_move(platform.window, RGFW_VECTOR(x, y));
+    RGFW_window_move(platform.window, RGFW_POINT(x, y));
 }
 
 // Set monitor for the current window
@@ -536,10 +532,10 @@
 // Get native window handle
 void *GetWindowHandle(void)
 {
-#ifndef RGFW_WINDOWS
-    return (void *)platform.window->src.window;
+#ifdef RGFW_WEBASM
+    return (void*)platform.window->src.ctx;
 #else
-    return platform.window->src.hwnd;
+    return (void*)platform.window->src.window;
 #endif
 }
 
@@ -643,7 +639,7 @@
 {
     RGFW_monitor monitor = RGFW_window_getMonitor(platform.window);
 
-    return (Vector2){((u32)monitor.scaleX)*platform.window->r.w, ((u32) monitor.scaleX)*platform.window->r.h};
+    return (Vector2){monitor.scaleX, monitor.scaleX};
 }
 
 // Set clipboard text content
@@ -689,10 +685,9 @@
 void DisableCursor(void)
 {
     RGFW_disableCursor = true;
-    
-    // Set cursor position in the middle
-    SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
 
+    RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0));
+
     HideCursor();
 }
 
@@ -745,7 +740,7 @@
 // Set mouse position XY
 void SetMousePosition(int x, int y)
 {
-    RGFW_window_moveMouse(platform.window, RGFW_VECTOR(x, y));
+    RGFW_window_moveMouse(platform.window, RGFW_POINT(x, y));
     CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
     CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
 }
@@ -756,6 +751,13 @@
     RGFW_window_setMouseStandard(platform.window, cursor);
 }
 
+// Get physical key name.
+const char *GetKeyName(int key)
+{
+    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
+    return "";
+}
+
 static KeyboardKey ConvertScancodeToKey(u32 keycode);
 
 // TODO: Review function to avoid duplicate with RSGL
@@ -868,10 +870,9 @@
     //-----------------------------------------------------------------------------
     CORE.Window.resizedLastFrame = false;
 
-#define RGFW_HOLD_MOUSE			(1L<<2)
-
-#if defined(RGFW_X11) //|| defined(RGFW_MACOS)
-    if (platform.window->src.winArgs & RGFW_HOLD_MOUSE)
+    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+    #define RGFW_HOLD_MOUSE     (1L<<2)
+    if (platform.window->_winArgs & RGFW_HOLD_MOUSE)
     {
         CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
         CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
@@ -880,11 +881,9 @@
     {
         CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
     }
-#endif
 
     while (RGFW_window_checkEvent(platform.window))
     {
-
         if ((platform.window->event.type >= RGFW_jsButtonPressed) && (platform.window->event.type <= RGFW_jsAxisMove))
         {
             if (!CORE.Input.Gamepad.ready[platform.window->event.joystick])
@@ -936,7 +935,7 @@
                 SetupViewport(platform.window->r.w, platform.window->r.h);
                 CORE.Window.screen.width = platform.window->r.w;
                 CORE.Window.screen.height =  platform.window->r.h;
-                CORE.Window.currentFbo.width = platform.window->r.w;;
+                CORE.Window.currentFbo.width = platform.window->r.w;
                 CORE.Window.currentFbo.height = platform.window->r.h;
                 CORE.Window.resizedLastFrame = true;
             } break;
@@ -1024,17 +1023,10 @@
             } break;
             case RGFW_mousePosChanged:
             {
-                if (platform.window->src.winArgs & RGFW_HOLD_MOUSE)
+                if (platform.window->_winArgs & RGFW_HOLD_MOUSE)
                 {
-                    CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
-
-                    if ((event->point.x - (platform.window->r.w/2))*2)
-                        CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.currentPosition.x;
-                    if ((event->point.y - (platform.window->r.h/2))*2)
-                        CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.currentPosition.y;
-
-                    CORE.Input.Mouse.currentPosition.x = (event->point.x - (platform.window->r.w/2))*2;
-                    CORE.Input.Mouse.currentPosition.y = (event->point.y - (platform.window->r.h/2))*2;
+                    CORE.Input.Mouse.currentPosition.x += (float)event->point.x;
+                    CORE.Input.Mouse.currentPosition.y += (float)event->point.y;
                 }
                 else
                 {
@@ -1198,12 +1190,9 @@
         }
 #endif
     }
-
-    if (RGFW_disableCursor && platform.window->event.inFocus) RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0));
     //-----------------------------------------------------------------------------
 }
 
-
 //----------------------------------------------------------------------------------
 // Module Internal Functions Definition
 //----------------------------------------------------------------------------------
@@ -1233,15 +1222,15 @@
     // Check selection OpenGL version
     if (rlGetVersion() == RL_OPENGL_21)
     {
-        RGFW_setGLVersion(2, 1);
+        RGFW_setGLVersion(RGFW_GL_CORE, 2, 1);
     }
     else if (rlGetVersion() == RL_OPENGL_33)
     {
-        RGFW_setGLVersion(3, 3);
+        RGFW_setGLVersion(RGFW_GL_CORE, 3, 3);
     }
     else if (rlGetVersion() == RL_OPENGL_43)
     {
-        RGFW_setGLVersion(4, 1);
+        RGFW_setGLVersion(RGFW_GL_CORE, 4, 1);
     }
 
     if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
@@ -1250,6 +1239,15 @@
     }
 
     platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags);
+
+    RGFW_area screenSize = RGFW_getScreenSize();
+    CORE.Window.display.width = screenSize.w;
+    CORE.Window.display.height = screenSize.h;
+    /* 
+        I think this is needed by Raylib now ? 
+        If so, rcore_destkop_sdl should be updated too
+    */
+    SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
 
     if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1);
 
diff --git a/raylib/src/platforms/rcore_desktop_sdl.c b/raylib/src/platforms/rcore_desktop_sdl.c
--- a/raylib/src/platforms/rcore_desktop_sdl.c
+++ b/raylib/src/platforms/rcore_desktop_sdl.c
@@ -58,6 +58,13 @@
 #endif
 
 //----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#ifndef MAX_CLIPBOARD_BUFFER_LENGTH
+    #define MAX_CLIPBOARD_BUFFER_LENGTH 1024 // Size of the clipboard buffer used on GetClipboardText()
+#endif
+
+//----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
 typedef struct {
@@ -852,10 +859,22 @@
 }
 
 // Get clipboard text content
-// NOTE: returned string must be freed with SDL_free()
 const char *GetClipboardText(void)
 {
-    return SDL_GetClipboardText();
+    static char buffer[MAX_CLIPBOARD_BUFFER_LENGTH] = { 0 };
+
+    char *clipboard = SDL_GetClipboardText();
+
+    int clipboardSize = snprintf(buffer, sizeof(buffer), "%s", clipboard);
+    if (clipboardSize >= MAX_CLIPBOARD_BUFFER_LENGTH)
+    {
+        char *truncate = buffer + MAX_CLIPBOARD_BUFFER_LENGTH - 4;
+        sprintf(truncate, "...");
+    }
+
+    SDL_free(clipboard);
+
+    return buffer;
 }
 
 // Show mouse cursor
@@ -966,6 +985,12 @@
     CORE.Input.Mouse.cursor = cursor;
 }
 
+// Get physical key name.
+const char *GetKeyName(int key)
+{
+    return SDL_GetKeyName(key);
+}
+
 static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event)
 {
     CORE.Input.Touch.pointCount = SDL_GetNumTouchFingers(event.touchId);
@@ -1583,8 +1608,8 @@
     // Initialize storage system
     //----------------------------------------------------------------------------
     // Define base path for storage
-    CORE.Storage.basePath = SDL_GetBasePath(); // Alternative: GetWorkingDirectory();  
-    CHDIR(CORE.Storage.basePath); 
+    CORE.Storage.basePath = SDL_GetBasePath(); // Alternative: GetWorkingDirectory();
+    CHDIR(CORE.Storage.basePath);
     //----------------------------------------------------------------------------
 
     TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully");
diff --git a/raylib/src/platforms/rcore_drm.c b/raylib/src/platforms/rcore_drm.c
--- a/raylib/src/platforms/rcore_drm.c
+++ b/raylib/src/platforms/rcore_drm.c
@@ -206,7 +206,7 @@
     [BTN_TL] = GAMEPAD_BUTTON_LEFT_TRIGGER_1,
     [BTN_TL2] = GAMEPAD_BUTTON_LEFT_TRIGGER_2,
     [BTN_TR] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
-    [BTN_TR2] GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
+    [BTN_TR2] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
     [BTN_SELECT] = GAMEPAD_BUTTON_MIDDLE_LEFT,
     [BTN_MODE] = GAMEPAD_BUTTON_MIDDLE,
     [BTN_START] = GAMEPAD_BUTTON_MIDDLE_RIGHT,
@@ -628,6 +628,13 @@
     TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
 }
 
+// Get physical key name.
+const char *GetKeyName(int key)
+{
+    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
+    return "";
+}
+
 // Register all input events
 void PollInputEvents(void)
 {
@@ -756,8 +763,10 @@
 
         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))
+        
+        // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected.
+        // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output.
+        if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id))
         {
             TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected");
             platform.connector = con;
@@ -1470,7 +1479,6 @@
             TEST_BIT(relBits, REL_Y) &&
             TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
     }
-
 
     if (TEST_BIT(evBits, EV_KEY))
     {
diff --git a/raylib/src/platforms/rcore_template.c b/raylib/src/platforms/rcore_template.c
--- a/raylib/src/platforms/rcore_template.c
+++ b/raylib/src/platforms/rcore_template.c
@@ -384,6 +384,13 @@
     TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
 }
 
+// Get physical key name.
+const char *GetKeyName(int key)
+{
+    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
+    return "";
+}
+
 // Register all input events
 void PollInputEvents(void)
 {
@@ -422,7 +429,6 @@
 
     // TODO: Poll input events for current platform
 }
-
 
 //----------------------------------------------------------------------------------
 // Module Internal Functions Definition
diff --git a/raylib/src/platforms/rcore_web.c b/raylib/src/platforms/rcore_web.c
--- a/raylib/src/platforms/rcore_web.c
+++ b/raylib/src/platforms/rcore_web.c
@@ -129,7 +129,7 @@
 
 // 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 EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
 static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
 
 // Emscripten input callback events
@@ -884,6 +884,13 @@
     }
 }
 
+// Get physical key name.
+const char *GetKeyName(int key)
+{
+    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
+    return "";
+}
+
 // Register all input events
 void PollInputEvents(void)
 {
@@ -928,7 +935,6 @@
     // 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 };
 
-
     // Gamepad support using emscripten API
     // NOTE: GLFW3 joystick functionality not available in web
 
@@ -974,7 +980,7 @@
                     default: break;
                 }
 
-                if (button != -1)   // Check for valid button
+                if (button + 1 != 0)   // Check for valid button
                 {
                     if (gamepadState.digitalButton[j] == 1)
                     {
@@ -1565,12 +1571,12 @@
 }
 
 // Register window resize event
-static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
-{
-    // TODO: Implement EmscriptenWindowResizedCallback()?
+// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
+// {
+//     // TODO: Implement EmscriptenWindowResizedCallback()?
 
-    return 1; // The event was consumed by the callback handler
-}
+//     return 1; // The event was consumed by the callback handler
+// }
 
 EM_JS(int, GetWindowInnerWidth, (), { return window.innerWidth; });
 EM_JS(int, GetWindowInnerHeight, (), { return window.innerHeight; });
@@ -1586,11 +1592,11 @@
     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 (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width;
+    else if (width > (int)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;
+    if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height;
+    else if (height > (int)CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height;
 
     emscripten_set_canvas_element_size("#canvas", width, height);
 
diff --git a/raylib/src/raudio.c b/raylib/src/raudio.c
--- a/raylib/src/raudio.c
+++ b/raylib/src/raudio.c
@@ -1626,7 +1626,9 @@
         {
             music.ctxType = MUSIC_AUDIO_FLAC;
             music.ctxData = ctxFlac;
-            music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels);
+            int sampleSize = ctxFlac->bitsPerSample;
+            if (ctxFlac->bitsPerSample == 24) sampleSize = 16;   // Forcing conversion to s16 on UpdateMusicStream()
+            music.stream = LoadAudioStream(ctxFlac->sampleRate, sampleSize, ctxFlac->channels);
             music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount;
             music.looping = true;   // Looping enabled by default
             musicLoaded = true;
diff --git a/raylib/src/raylib.h b/raylib/src/raylib.h
--- a/raylib/src/raylib.h
+++ b/raylib/src/raylib.h
@@ -1,6 +1,6 @@
 /**********************************************************************************************
 *
-*   raylib v5.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
+*   raylib v5.5-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
@@ -84,7 +84,7 @@
 #define RAYLIB_VERSION_MAJOR 5
 #define RAYLIB_VERSION_MINOR 5
 #define RAYLIB_VERSION_PATCH 0
-#define RAYLIB_VERSION  "5.5"
+#define RAYLIB_VERSION  "5.5-dev"
 
 // Function specifiers in case library is build/used as a shared library
 // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
@@ -352,8 +352,10 @@
     // Animation vertex data
     float *animVertices;    // Animated vertex positions (after bones transformations)
     float *animNormals;     // Animated normals (after bones transformations)
-    unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning)
-    float *boneWeights;     // Vertex bone weight, up to 4 bones influence by vertex (skinning)
+    unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)
+    float *boneWeights;     // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)
+    Matrix *boneMatrices;   // Bones animated transformation matrices
+    int boneCount;          // Number of bones
 
     // OpenGL identifiers
     unsigned int vaoId;     // OpenGL Vertex Array Object id
@@ -790,7 +792,10 @@
     SHADER_LOC_MAP_CUBEMAP,         // Shader location: samplerCube texture: cubemap
     SHADER_LOC_MAP_IRRADIANCE,      // Shader location: samplerCube texture: irradiance
     SHADER_LOC_MAP_PREFILTER,       // Shader location: samplerCube texture: prefilter
-    SHADER_LOC_MAP_BRDF             // Shader location: sampler2d texture: brdf
+    SHADER_LOC_MAP_BRDF,            // Shader location: sampler2d texture: brdf
+    SHADER_LOC_VERTEX_BONEIDS,      // Shader location: vertex attribute: boneIds
+    SHADER_LOC_VERTEX_BONEWEIGHTS,  // Shader location: vertex attribute: boneWeights
+    SHADER_LOC_BONE_MATRICES        // Shader location: array of matrices uniform: boneMatrices
 } ShaderLocationIndex;
 
 #define SHADER_LOC_MAP_DIFFUSE      SHADER_LOC_MAP_ALBEDO
@@ -968,8 +973,8 @@
 RLAPI bool IsWindowState(unsigned int flag);                      // Check if one specific window flag is enabled
 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 ToggleFullscreen(void);                                // Toggle window state: fullscreen/windowed [resizes monitor to match window resolution] (only PLATFORM_DESKTOP)
+RLAPI void ToggleBorderlessWindowed(void);                        // Toggle window state: borderless windowed [resizes window to match monitor resolution] (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)
@@ -1122,11 +1127,12 @@
 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 of the running application (uses static string)
+RLAPI int MakeDirectory(const char *dirPath);                     // Create directories (including full path requested), returns 0 on success
 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 bool IsFileNameValid(const char *fileName);                 // Check if fileName is valid for the platform/OS
 RLAPI FilePathList LoadDirectoryFiles(const char *dirPath);       // Load directory filepaths
-RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan
+RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result
 RLAPI void UnloadDirectoryFiles(FilePathList files);              // Unload filepaths
 RLAPI bool IsFileDropped(void);                                   // Check if a file has been dropped into window
 RLAPI FilePathList LoadDroppedFiles(void);                        // Load dropped filepaths
@@ -1228,8 +1234,8 @@
 RLAPI Rectangle GetShapesTextureRectangle(void);                        // Get texture source rectangle that is used for shapes drawing
 
 // Basic shapes drawing functions
-RLAPI void DrawPixel(int posX, int posY, Color color);                                                   // Draw a pixel
-RLAPI void DrawPixelV(Vector2 position, Color color);                                                    // Draw a pixel (Vector version)
+RLAPI void DrawPixel(int posX, int posY, Color color);                                                   // Draw a pixel using geometry [Can be slow, use with care]
+RLAPI void DrawPixelV(Vector2 position, Color color);                                                    // Draw a pixel using geometry (Vector version) [Can be slow, use with care]
 RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color);                // Draw a line
 RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color);                                     // Draw a line (using gl lines)
 RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color);                       // Draw a line (using triangles/quads)
@@ -1238,7 +1244,7 @@
 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
 RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline
-RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2);       // Draw a gradient-filled circle
+RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer);         // 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)
@@ -1250,9 +1256,9 @@
 RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color);                                  // Draw a color-filled rectangle (Vector version)
 RLAPI void DrawRectangleRec(Rectangle rec, Color color);                                                 // Draw a color-filled rectangle
 RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color);                 // Draw a color-filled rectangle with pro parameters
-RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle
-RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle
-RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4);       // Draw a gradient-filled rectangle with custom vertex colors
+RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom);   // Draw a vertical-gradient-filled rectangle
+RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right);   // Draw a horizontal-gradient-filled rectangle
+RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors
 RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color);                   // Draw rectangle outline
 RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color);                            // Draw rectangle outline with extended parameters
 RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color);              // Draw rectangle with rounded edges
@@ -1306,7 +1312,6 @@
 // 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 *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 LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames); // Load image sequence from memory buffer
 RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize);      // Load image from memory buffer, fileType refers to extension: i.e. '.png'
@@ -1431,6 +1436,7 @@
 RLAPI Color ColorContrast(Color color, float contrast);                     // Get color with contrast correction, contrast values between -1.0f and 1.0f
 RLAPI Color ColorAlpha(Color color, float alpha);                           // Get color with alpha applied, alpha goes from 0.0f to 1.0f
 RLAPI Color ColorAlphaBlend(Color dst, Color src, Color tint);              // Get src alpha-blended into dst color with tint
+RLAPI Color ColorLerp(Color color1, Color color2, float factor);            // Get color lerp interpolation between two colors, factor [0.0f..1.0f]
 RLAPI Color GetColor(unsigned int hexValue);                                // Get Color structure from hexadecimal value
 RLAPI Color GetPixelColor(void *srcPtr, int format);                        // Get Color from a source pixel pointer of certain format
 RLAPI void SetPixelColor(void *dstPtr, Color color, int format);            // Set color formatted into destination pixel pointer
@@ -1443,7 +1449,7 @@
 // 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 *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 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, font size is provided in pixels height
 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 *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
@@ -1545,6 +1551,8 @@
 RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters
 RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint);          // Draw a model wires (with texture if set)
 RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters
+RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points
+RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters
 RLAPI void DrawBoundingBox(BoundingBox box, Color color);                                   // Draw bounding box (wires)
 RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint);   // Draw a billboard texture
 RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source
@@ -1588,6 +1596,7 @@
 RLAPI void UnloadModelAnimation(ModelAnimation anim);                                       // Unload animation data
 RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount);                // Unload animation array data
 RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim);                         // Check model animation skeleton match
+RLAPI void UpdateModelAnimationBoneMatrices(Model model, ModelAnimation anim, int frame);   // Update model animation mesh bone matrices (Note GPU skinning does not work on Mac)
 
 // Collision detection functions
 RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2);   // Check collision between two spheres
diff --git a/raylib/src/rcamera.h b/raylib/src/rcamera.h
--- a/raylib/src/rcamera.h
+++ b/raylib/src/rcamera.h
@@ -162,7 +162,6 @@
 
 #endif // RCAMERA_H
 
-
 /***********************************************************************************
 *
 *   CAMERA IMPLEMENTATION
@@ -196,12 +195,12 @@
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-#define CAMERA_MOVE_SPEED                               0.09f
+#define CAMERA_MOVE_SPEED                               5.4f       // Units per second
 #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
 
 // Camera orbital speed in CAMERA_ORBITAL mode
 #define CAMERA_ORBITAL_SPEED                            0.5f       // Radians per second
@@ -444,11 +443,17 @@
     bool lockView = ((mode == CAMERA_FREE) || (mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL));
     bool rotateUp = false;
 
+    // Camera speeds based on frame time
+    float cameraMoveSpeed = CAMERA_MOVE_SPEED*GetFrameTime();
+    float cameraRotationSpeed = CAMERA_ROTATION_SPEED*GetFrameTime();
+    float cameraPanSpeed = CAMERA_PAN_SPEED*GetFrameTime();
+    float cameraOrbitalSpeed = CAMERA_ORBITAL_SPEED*GetFrameTime();
+
     if (mode == CAMERA_CUSTOM) {}
     else if (mode == CAMERA_ORBITAL)
     {
         // Orbital can just orbit
-        Matrix rotation = MatrixRotate(GetCameraUp(camera), CAMERA_ORBITAL_SPEED*GetFrameTime());
+        Matrix rotation = MatrixRotate(GetCameraUp(camera), cameraOrbitalSpeed);
         Vector3 view = Vector3Subtract(camera->position, camera->target);
         view = Vector3Transform(view, rotation);
         camera->position = Vector3Add(camera->target, view);
@@ -456,22 +461,22 @@
     else
     {
         // Camera rotation
-        if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp);
-        if (IsKeyDown(KEY_UP)) CameraPitch(camera, CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp);
-        if (IsKeyDown(KEY_RIGHT)) CameraYaw(camera, -CAMERA_ROTATION_SPEED, rotateAroundTarget);
-        if (IsKeyDown(KEY_LEFT)) CameraYaw(camera, CAMERA_ROTATION_SPEED, rotateAroundTarget);
-        if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED);
-        if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED);
+        if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -cameraRotationSpeed, lockView, rotateAroundTarget, rotateUp);
+        if (IsKeyDown(KEY_UP)) CameraPitch(camera, cameraRotationSpeed, lockView, rotateAroundTarget, rotateUp);
+        if (IsKeyDown(KEY_RIGHT)) CameraYaw(camera, -cameraRotationSpeed, rotateAroundTarget);
+        if (IsKeyDown(KEY_LEFT)) CameraYaw(camera, cameraRotationSpeed, rotateAroundTarget);
+        if (IsKeyDown(KEY_Q)) CameraRoll(camera, -cameraRotationSpeed);
+        if (IsKeyDown(KEY_E)) CameraRoll(camera, cameraRotationSpeed);
 
         // Camera movement
         // 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);
+            if (mouseDelta.x > 0.0f) CameraMoveRight(camera, cameraPanSpeed, moveInWorldPlane);
+            if (mouseDelta.x < 0.0f) CameraMoveRight(camera, -cameraPanSpeed, moveInWorldPlane);
+            if (mouseDelta.y > 0.0f) CameraMoveUp(camera, -cameraPanSpeed);
+            if (mouseDelta.y < 0.0f) CameraMoveUp(camera, cameraPanSpeed);
         }
         else
         {
@@ -481,10 +486,10 @@
         }
 
         // 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);
-        if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane);
+        if (IsKeyDown(KEY_W)) CameraMoveForward(camera, cameraMoveSpeed, moveInWorldPlane);
+        if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -cameraMoveSpeed, moveInWorldPlane);
+        if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -cameraMoveSpeed, moveInWorldPlane);
+        if (IsKeyDown(KEY_D)) CameraMoveRight(camera, cameraMoveSpeed, moveInWorldPlane);
 
         // Gamepad movement
         if (IsGamepadAvailable(0))
@@ -493,16 +498,16 @@
             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.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 (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, cameraMoveSpeed, moveInWorldPlane);
+            if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -cameraMoveSpeed, moveInWorldPlane);
+            if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.25f) CameraMoveForward(camera, -cameraMoveSpeed, moveInWorldPlane);
+            if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, cameraMoveSpeed, moveInWorldPlane);
         }
 
         if (mode == CAMERA_FREE)
         {
-            if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED);
-            if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED);
+            if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, cameraMoveSpeed);
+            if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -cameraMoveSpeed);
         }
     }
 
diff --git a/raylib/src/rcore.c b/raylib/src/rcore.c
--- a/raylib/src/rcore.c
+++ b/raylib/src/rcore.c
@@ -165,6 +165,10 @@
 unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
 #elif defined(__linux__)
     #include <unistd.h>
+#elif defined(__FreeBSD__)
+    #include <sys/types.h>
+    #include <sys/sysctl.h>
+    #include <unistd.h>
 #elif defined(__APPLE__)
     #include <sys/syslimits.h>
     #include <mach-o/dyld.h>
@@ -187,14 +191,16 @@
 #endif
 
 #if defined(_WIN32)
-    #include <direct.h>             // Required for: _getch(), _chdir()
+    #include <io.h>                 // Required for: _access() [Used in FileExists()]
+    #include <direct.h>             // Required for: _getch(), _chdir(), _mkdir()
     #define GETCWD _getcwd          // NOTE: MSDN recommends not to use getcwd(), chdir()
     #define CHDIR _chdir
-    #include <io.h>                 // Required for: _access() [Used in FileExists()]
+    #define MKDIR(dir) _mkdir(dir)
 #else
-    #include <unistd.h>             // Required for: getch(), chdir() (POSIX), access()
+    #include <unistd.h>             // Required for: getch(), chdir(), mkdir(), access()
     #define GETCWD getcwd
     #define CHDIR chdir
+    #define MKDIR(dir) mkdir(dir, 0777)
 #endif
 
 //----------------------------------------------------------------------------------
@@ -247,6 +253,10 @@
     #define MAX_AUTOMATION_EVENTS      16384        // Maximum number of automation events to record
 #endif
 
+#ifndef DIRECTORY_FILTER_TAG
+    #define DIRECTORY_FILTER_TAG       "DIR"        // Name tag used to request directory inclusion on directory scan
+#endif                                              // NOTE: Used in ScanDirectoryFiles(), ScanDirectoryFilesRecursively() and LoadDirectoryFilesEx()
+
 // Flags operation macros
 #define FLAG_SET(n, f) ((n) |= (f))
 #define FLAG_CLEAR(n, f) ((n) &= ~(f))
@@ -360,16 +370,21 @@
 //----------------------------------------------------------------------------------
 RLAPI const char *raylib_version = RAYLIB_VERSION;  // raylib version exported symbol, required for some bindings
 
-CoreData CORE = { 0 };               // Global CORE state context
+CoreData CORE = { 0 };                      // Global CORE state context
 
+// Flag to note GPU acceleration is available,
+// referenced from other modules to support GPU data loading
+// NOTE: Useful to allow Texture, RenderTexture, Font.texture, Mesh.vaoId/vboId, Shader loading
+bool isGpuReady = false;
+
 #if defined(SUPPORT_SCREEN_CAPTURE)
-static int screenshotCounter = 0;    // Screenshots counter
+static int screenshotCounter = 0;           // Screenshots counter
 #endif
 
 #if defined(SUPPORT_GIF_RECORDING)
-unsigned int gifFrameCounter = 0;    // GIF frames counter
-bool gifRecording = false;           // GIF recording state
-MsfGifState gifState = { 0 };        // MSGIF context state
+static unsigned int gifFrameCounter = 0;    // GIF frames counter
+static bool gifRecording = false;           // GIF recording state
+static MsfGifState gifState = { 0 };        // MSGIF context state
 #endif
 
 #if defined(SUPPORT_AUTOMATION_EVENTS)
@@ -563,7 +578,6 @@
 //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);
@@ -637,28 +651,31 @@
     // 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);
+    isGpuReady = true; // Flag to note GPU has been initialized successfully
 
     // 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 });
-    }
+#if defined(SUPPORT_MODULE_RTEXT)
+    #if 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
     #endif
 #else
     #if defined(SUPPORT_MODULE_RSHAPES)
@@ -668,21 +685,14 @@
     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
 
     CORE.Time.frameCounter = 0;
     CORE.Window.shouldClose = false;
 
     // Initialize random seed
     SetRandomSeed((unsigned int)time(NULL));
+    
+    TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", GetWorkingDirectory());
 }
 
 // Close window and unload OpenGL context
@@ -1294,6 +1304,8 @@
         //          vertex color location       = 3
         //          vertex tangent location     = 4
         //          vertex texcoord2 location   = 5
+        //          vertex boneIds location     = 6
+        //          vertex boneWeights location = 7
 
         // NOTE: If any location is not found, loc point becomes -1
 
@@ -1309,6 +1321,8 @@
         shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
         shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
         shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
+        shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
+        shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
 
         // Get handles to GLSL uniform locations (vertex shader)
         shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
@@ -1316,6 +1330,7 @@
         shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
         shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
         shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
+        shader.locs[SHADER_LOC_BONE_MATRICES] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES);
 
         // Get handles to GLSL uniform locations (fragment shader)
         shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
@@ -2160,6 +2175,28 @@
         appDir[0] = '.';
         appDir[1] = '/';
     }
+#elif defined(__FreeBSD__)
+    size_t size = sizeof(appDir);
+    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+
+    if (sysctl(mib, 4, appDir, &size, NULL, 0) == 0)
+    {
+        int len = strlen(appDir);
+        for (int i = len; i >= 0; --i)
+        {
+            if (appDir[i] == '/')
+            {
+                appDir[i + 1] = '\0';
+                break;
+            }
+        }
+    }
+    else
+    {
+        appDir[0] = '.';
+        appDir[1] = '/';
+    }
+
 #endif
 
     return appDir;
@@ -2231,6 +2268,40 @@
     RL_FREE(files.paths);
 }
 
+// Create directories (including full path requested), returns 0 on success
+int MakeDirectory(const char *dirPath)
+{
+    if ((dirPath == NULL) || (dirPath[0] == '\0')) return 1; // Path is not valid
+    if (DirectoryExists(dirPath)) return 0; // Path already exists (is valid)
+
+    // Copy path string to avoid modifying original
+    int len = (int)strlen(dirPath) + 1;
+    char *pathcpy = (char *)RL_CALLOC(len, 1);
+    memcpy(pathcpy, dirPath, len);
+
+    // Iterate over pathcpy, create each subdirectory as needed
+    for (int i = 0; (i < len) && (pathcpy[i] != '\0'); i++)
+    {
+        if (pathcpy[i] == ':') i++;
+        else
+        {
+            if ((pathcpy[i] == '\\') || (pathcpy[i] == '/'))
+            {
+                pathcpy[i] = '\0';
+                if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
+                pathcpy[i] = '/';
+            }
+        }
+    }
+
+    // Create final directory
+    if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
+
+    RL_FREE(pathcpy);
+
+    return 0;
+}
+
 // Change working directory, returns true on success
 bool ChangeDirectory(const char *dir)
 {
@@ -2713,8 +2784,8 @@
             } 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;
+                CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0];
+                CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1];
             } 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
@@ -2751,6 +2822,8 @@
             case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break;
             default: break;
         }
+
+        TRACELOG(LOG_INFO, "AUTOMATION PLAY: Frame: %i | Event type: %i | Event parameters: %i, %i, %i", event.frame, event.type, event.params[0], event.params[1], event.params[2]);
     }
 #endif
 }
@@ -2958,11 +3031,15 @@
 // Get axis movement vector for a gamepad
 float GetGamepadAxisMovement(int gamepad, int axis)
 {
-    float value = 0;
+    float value = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
 
-    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
+    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS)) {
+        float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]);
 
+        // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA
+        if (movement > value + 0.1f) value = CORE.Input.Gamepad.axisState[gamepad][axis];
+    }
+
     return value;
 }
 
@@ -3306,11 +3383,22 @@
 
                 if (filter != NULL)
                 {
-                    if (IsFileExtension(path, filter))
+                    if (IsPathFile(path))
                     {
-                        strcpy(files->paths[files->count], path);
-                        files->count++;
+                        if (IsFileExtension(path, filter))
+                        {
+                            strcpy(files->paths[files->count], path);
+                            files->count++;
+                        }
                     }
+                    else
+                    {
+                        if (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0)
+                        {
+                            strcpy(files->paths[files->count], path);
+                            files->count++;
+                        }
+                    }
                 }
                 else
                 {
@@ -3369,7 +3457,22 @@
                         break;
                     }
                 }
-                else ScanDirectoryFilesRecursively(path, files, filter);
+                else 
+                {
+                    if ((filter != NULL) && (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0))
+                    {
+                        strcpy(files->paths[files->count], path);
+                        files->count++;
+                    }
+                    
+                    if (files->count >= files->capacity)
+                    {
+                        TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
+                        break;
+                    }
+
+                    ScanDirectoryFilesRecursively(path, files, filter);
+                }
             }
         }
 
@@ -3482,7 +3585,7 @@
         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[1] = (int)CORE.Input.Mouse.currentWheelMove.y;
         currentEventList->events[currentEventList->count].params[2] = 0;
 
         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]);
@@ -3605,7 +3708,8 @@
         for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
         {
             // Event type: INPUT_GAMEPAD_AXIS_MOTION
-            if (CORE.Input.Gamepad.axisState[gamepad][axis] > 0.1f)
+            float defaultMovement = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
+            if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement)
             {
                 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
                 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION;
diff --git a/raylib/src/rlgl.h b/raylib/src/rlgl.h
--- a/raylib/src/rlgl.h
+++ b/raylib/src/rlgl.h
@@ -68,12 +68,15 @@
 *       #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR        "vertexColor"       // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
 *       #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT      "vertexTangent"     // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
 *       #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2    "vertexTexCoord2"   // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
+*       #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS      "vertexBoneIds"     // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS
+*       #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS  "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
 *       #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_UNIFORM_NAME_BONE_MATRICES  "boneMatrices"   // bone matrices
 *       #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)
@@ -324,24 +327,36 @@
 
 // Default shader vertex attribute locations
 #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
-    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION  0
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION    0
 #endif
 #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
-    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD  1
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD    1
 #endif
 #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
-    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL    2
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL      2
 #endif
 #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
-    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR     3
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR       3
 #endif
     #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT       4
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT         4
 #endif
 #ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
-    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2   5
 #endif
+#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES     6
+#endif
 
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS     7
+#endif
+#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
+#endif
+#endif
+
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
@@ -527,6 +542,10 @@
     RL_SHADER_UNIFORM_IVEC2,            // Shader uniform type: ivec2 (2 int)
     RL_SHADER_UNIFORM_IVEC3,            // Shader uniform type: ivec3 (3 int)
     RL_SHADER_UNIFORM_IVEC4,            // Shader uniform type: ivec4 (4 int)
+    RL_SHADER_UNIFORM_UINT,             // Shader uniform type: unsigned int
+    RL_SHADER_UNIFORM_UIVEC2,           // Shader uniform type: uivec2 (2 unsigned int)
+    RL_SHADER_UNIFORM_UIVEC3,           // Shader uniform type: uivec3 (3 unsigned int)
+    RL_SHADER_UNIFORM_UIVEC4,           // Shader uniform type: uivec4 (4 unsigned int)
     RL_SHADER_UNIFORM_SAMPLER2D         // Shader uniform type: sampler2d
 } rlShaderUniformDataType;
 
@@ -665,7 +684,7 @@
 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 (and point) mode
 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
@@ -755,6 +774,7 @@
 RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName);   // Get shader location attribute
 RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count); // Set shader value uniform
 RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat);                        // Set shader value matrix
+RLAPI void rlSetUniformMatrices(int locIndex, const Matrix *mat, int count);    // Set shader value matrices
 RLAPI void rlSetUniformSampler(int locIndex, unsigned int textureId);           // Set shader value sampler
 RLAPI void rlSetShader(unsigned int id, int *locs);                             // Set shader currently active (id and locations)
 
@@ -973,6 +993,12 @@
 #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
     #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2    "vertexTexCoord2"   // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
 #endif
+#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS
+    #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS      "vertexBoneIds"     // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS
+#endif
+#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS
+    #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS  "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS
+#endif
 
 #ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP
     #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP         "mvp"               // model-view-projection matrix
@@ -992,6 +1018,9 @@
 #ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR
     #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR       "colDiffuse"        // color diffuse (base tint color, multiplied by texture color)
 #endif
+#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES
+    #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES  "boneMatrices"   // bone matrices
+#endif
 #ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0
     #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0  "texture0"          // texture0 (texture slot active 0)
 #endif
@@ -1948,6 +1977,7 @@
 #endif
 }
 
+// Enable point mode
 void rlEnablePointMode(void)
 {
 #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
@@ -1956,6 +1986,7 @@
     glEnable(GL_PROGRAM_POINT_SIZE);
 #endif
 }
+
 // Disable wire mode
 void rlDisableWireMode(void)
 {
@@ -3994,18 +4025,18 @@
     unsigned int fragmentShaderId = 0;
 
     // Compile vertex shader (if provided)
+    // NOTE: If not vertex shader is provided, use default one
     if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER);
-    // In case no vertex shader was provided or compilation failed, we use default vertex shader
-    if (vertexShaderId == 0) vertexShaderId = RLGL.State.defaultVShaderId;
+    else vertexShaderId = RLGL.State.defaultVShaderId;
 
     // Compile fragment shader (if provided)
+    // NOTE: If not vertex shader is provided, use default one
     if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER);
-    // In case no fragment shader was provided or compilation failed, we use default fragment shader
-    if (fragmentShaderId == 0) fragmentShaderId = RLGL.State.defaultFShaderId;
+    else fragmentShaderId = RLGL.State.defaultFShaderId;
 
     // In case vertex and fragment shader are the default ones, no need to recompile, we can just assign the default shader program id
     if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId;
-    else
+    else if ((vertexShaderId > 0) && (fragmentShaderId > 0))
     {
         // One of or both shader are new, we need to compile a new shader program
         id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId);
@@ -4083,6 +4114,8 @@
             //case GL_GEOMETRY_SHADER:
         #if defined(GRAPHICS_API_OPENGL_43)
             case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile compute shader code", shader); break;
+        #elif defined(GRAPHICS_API_OPENGL_33)
+            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break;
         #endif
             default: break;
         }
@@ -4098,6 +4131,8 @@
             TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shader, log);
             RL_FREE(log);
         }
+
+        shader = 0;
     }
     else
     {
@@ -4108,6 +4143,8 @@
             //case GL_GEOMETRY_SHADER:
         #if defined(GRAPHICS_API_OPENGL_43)
             case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader compiled successfully", shader); break;
+        #elif defined(GRAPHICS_API_OPENGL_33)
+            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break;
         #endif
             default: break;
         }
@@ -4137,6 +4174,11 @@
     glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
     glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
 
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
+    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
+#endif
+
     // NOTE: If some attrib name is no found on the shader, it locations becomes -1
 
     glLinkProgram(program);
@@ -4228,8 +4270,16 @@
         case RL_SHADER_UNIFORM_IVEC2: glUniform2iv(locIndex, count, (int *)value); break;
         case RL_SHADER_UNIFORM_IVEC3: glUniform3iv(locIndex, count, (int *)value); break;
         case RL_SHADER_UNIFORM_IVEC4: glUniform4iv(locIndex, count, (int *)value); break;
+    #if !defined(GRAPHICS_API_OPENGL_ES2)
+        case RL_SHADER_UNIFORM_UINT: glUniform1uiv(locIndex, count, (unsigned int *)value); break;
+        case RL_SHADER_UNIFORM_UIVEC2: glUniform2uiv(locIndex, count, (unsigned int *)value); break;
+        case RL_SHADER_UNIFORM_UIVEC3: glUniform3uiv(locIndex, count, (unsigned int *)value); break;
+        case RL_SHADER_UNIFORM_UIVEC4: glUniform4uiv(locIndex, count, (unsigned int *)value); break;
+    #endif
         case RL_SHADER_UNIFORM_SAMPLER2D: glUniform1iv(locIndex, count, (int *)value); break;
         default: TRACELOG(RL_LOG_WARNING, "SHADER: Failed to set uniform value, data type not recognized");
+
+        // TODO: Support glUniform1uiv(), glUniform2uiv(), glUniform3uiv(), glUniform4uiv()
     }
 #endif
 }
@@ -4263,6 +4313,14 @@
 #endif
 }
 
+// Set shader value uniform matrix
+void rlSetUniformMatrices(int locIndex, const Matrix *matrices, int count)
+{
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+    glUniformMatrix4fv(locIndex, count, true, (const float*)matrices);
+#endif
+}
+
 // Set shader value uniform sampler
 void rlSetUniformSampler(int locIndex, unsigned int textureId)
 {
@@ -4348,6 +4406,8 @@
 
         TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", program);
     }
+#else
+    TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43");
 #endif
 
     return program;
@@ -4372,6 +4432,8 @@
     glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY);
     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);
+#else
+    TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43");
 #endif
 
     return ssbo;
@@ -4382,7 +4444,10 @@
 {
 #if defined(GRAPHICS_API_OPENGL_43)
     glDeleteBuffers(1, &ssboId);
+#else
+    TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43");
 #endif
+
 }
 
 // Update SSBO buffer data
@@ -4397,14 +4462,14 @@
 // Get SSBO buffer size
 unsigned int rlGetShaderBufferSize(unsigned int id)
 {
-    long long size = 0;
-
 #if defined(GRAPHICS_API_OPENGL_43)
+    GLint64 size = 0;
     glBindBuffer(GL_SHADER_STORAGE_BUFFER, id);
     glGetBufferParameteri64v(GL_SHADER_STORAGE_BUFFER, GL_BUFFER_SIZE, &size);
-#endif
-
     return (size > 0)? (unsigned int)size : 0;
+#else
+    return 0;
+#endif
 }
 
 // Read SSBO buffer data (GPU->CPU)
@@ -4442,6 +4507,8 @@
 
     rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
     glBindImageTexture(index, id, 0, 0, 0, readonly? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat);
+#else
+    TRACELOG(RL_LOG_WARNING, "TEXTURE: Image texture binding not enabled. Define GRAPHICS_API_OPENGL_43");
 #endif
 }
 
@@ -4756,7 +4823,16 @@
     "out vec2 fragTexCoord;             \n"
     "out vec4 fragColor;                \n"
 #endif
-#if defined(GRAPHICS_API_OPENGL_ES2)
+
+#if defined(GRAPHICS_API_OPENGL_ES3)
+    "#version 300 es                    \n"
+    "precision mediump float;           \n"     // Precision required for OpenGL ES3 (WebGL 2) (on some browsers)
+    "in vec3 vertexPosition;            \n"
+    "in vec2 vertexTexCoord;            \n"
+    "in vec4 vertexColor;               \n"
+    "out vec2 fragTexCoord;             \n"
+    "out vec4 fragColor;                \n"
+#elif 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"
@@ -4765,6 +4841,7 @@
     "varying vec2 fragTexCoord;         \n"
     "varying vec4 fragColor;            \n"
 #endif
+
     "uniform mat4 mvp;                  \n"
     "void main()                        \n"
     "{                                  \n"
@@ -4799,7 +4876,21 @@
     "    finalColor = texelColor*colDiffuse*fragColor;        \n"
     "}                                  \n";
 #endif
-#if defined(GRAPHICS_API_OPENGL_ES2)
+
+#if defined(GRAPHICS_API_OPENGL_ES3)
+    "#version 300 es                    \n"
+    "precision mediump float;           \n"     // Precision required for OpenGL ES3 (WebGL 2)
+    "in vec2 fragTexCoord;              \n"
+    "in vec4 fragColor;                 \n"
+    "out vec4 finalColor;               \n"
+    "uniform sampler2D texture0;        \n"
+    "uniform vec4 colDiffuse;           \n"
+    "void main()                        \n"
+    "{                                  \n"
+    "    vec4 texelColor = texture(texture0, fragTexCoord);   \n"
+    "    finalColor = texelColor*colDiffuse*fragColor;        \n"
+    "}                                  \n";
+#elif defined(GRAPHICS_API_OPENGL_ES2)
     "#version 100                       \n"
     "precision mediump float;           \n"     // Precision required for OpenGL ES2 (WebGL)
     "varying vec2 fragTexCoord;         \n"
@@ -4969,7 +5060,8 @@
         default: break;
     }
 
-    dataSize = width*height*bpp/8;  // Total data size in bytes
+    double bytesPerPixel = (double)bpp/8.0;
+    dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
 
     // Most compressed formats works on 4x4 blocks,
     // if texture is smaller, minimum dataSize is 8 or 16
diff --git a/raylib/src/rmodels.c b/raylib/src/rmodels.c
--- a/raylib/src/rmodels.c
+++ b/raylib/src/rmodels.c
@@ -130,7 +130,7 @@
     #define MAX_MATERIAL_MAPS       12    // Maximum number of maps supported
 #endif
 #ifndef MAX_MESH_VERTEX_BUFFERS
-    #define MAX_MESH_VERTEX_BUFFERS  7    // Maximum vertex buffers (VBO) per mesh
+    #define MAX_MESH_VERTEX_BUFFERS  9    // Maximum vertex buffers (VBO) per mesh
 #endif
 
 //----------------------------------------------------------------------------------
@@ -702,7 +702,6 @@
     rlPopMatrix();
 }
 
-
 // Draw a wired cylinder with base at startPos and top at endPos
 // NOTE: It could be also used for pyramid and cone
 void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color)
@@ -1246,14 +1245,19 @@
     mesh->vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int));
 
     mesh->vaoId = 0;        // Vertex Array Object
-    mesh->vboId[0] = 0;     // Vertex buffer: positions
-    mesh->vboId[1] = 0;     // Vertex buffer: texcoords
-    mesh->vboId[2] = 0;     // Vertex buffer: normals
-    mesh->vboId[3] = 0;     // Vertex buffer: colors
-    mesh->vboId[4] = 0;     // Vertex buffer: tangents
-    mesh->vboId[5] = 0;     // Vertex buffer: texcoords2
-    mesh->vboId[6] = 0;     // Vertex buffer: indices
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = 0;     // Vertex buffer: positions
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = 0;     // Vertex buffer: texcoords
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = 0;       // Vertex buffer: normals
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] = 0;        // Vertex buffer: colors
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = 0;      // Vertex buffer: tangents
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = 0;    // Vertex buffer: texcoords2
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = 0;      // Vertex buffer: indices
 
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = 0;      // Vertex buffer: boneIds
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = 0;  // Vertex buffer: boneWeights
+#endif
+
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     mesh->vaoId = rlLoadVertexArray();
     rlEnableVertexArray(mesh->vaoId);
@@ -1262,12 +1266,12 @@
 
     // Enable vertex attributes: position (shader-location = 0)
     void *vertices = (mesh->animVertices != NULL)? mesh->animVertices : mesh->vertices;
-    mesh->vboId[0] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic);
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic);
     rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, 3, RL_FLOAT, 0, 0, 0);
     rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION);
 
     // Enable vertex attributes: texcoords (shader-location = 1)
-    mesh->vboId[1] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic);
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic);
     rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, RL_FLOAT, 0, 0, 0);
     rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD);
 
@@ -1278,7 +1282,7 @@
     {
         // Enable vertex attributes: normals (shader-location = 2)
         void *normals = (mesh->animNormals != NULL)? mesh->animNormals : mesh->normals;
-        mesh->vboId[2] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic);
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic);
         rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, 3, RL_FLOAT, 0, 0, 0);
         rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL);
     }
@@ -1294,7 +1298,7 @@
     if (mesh->colors != NULL)
     {
         // Enable vertex attribute: color (shader-location = 3)
-        mesh->vboId[3] = rlLoadVertexBuffer(mesh->colors, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] = rlLoadVertexBuffer(mesh->colors, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
         rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, 4, RL_UNSIGNED_BYTE, 1, 0, 0);
         rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR);
     }
@@ -1310,7 +1314,7 @@
     if (mesh->tangents != NULL)
     {
         // Enable vertex attribute: tangent (shader-location = 4)
-        mesh->vboId[4] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), dynamic);
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), dynamic);
         rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0);
         rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT);
     }
@@ -1326,7 +1330,7 @@
     if (mesh->texcoords2 != NULL)
     {
         // Enable vertex attribute: texcoord2 (shader-location = 5)
-        mesh->vboId[5] = rlLoadVertexBuffer(mesh->texcoords2, mesh->vertexCount*2*sizeof(float), dynamic);
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = rlLoadVertexBuffer(mesh->texcoords2, mesh->vertexCount*2*sizeof(float), dynamic);
         rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, 2, RL_FLOAT, 0, 0, 0);
         rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2);
     }
@@ -1339,9 +1343,43 @@
         rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2);
     }
 
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+    if (mesh->boneIds != NULL)
+    {
+        // Enable vertex attribute: boneIds (shader-location = 6)
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = rlLoadVertexBuffer(mesh->boneIds, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, 4, RL_UNSIGNED_BYTE, 0, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
+    }
+    else
+    {
+        // Default vertex attribute: boneIds
+        // WARNING: Default value provided to shader if location available
+        float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, value, SHADER_ATTRIB_VEC4, 4);
+        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
+    }
+    
+    if (mesh->boneWeights != NULL)
+    {
+        // Enable vertex attribute: boneWeights (shader-location = 7)
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = rlLoadVertexBuffer(mesh->boneWeights, mesh->vertexCount*4*sizeof(float), dynamic);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, 4, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS);
+    }
+    else
+    {
+        // Default vertex attribute: boneWeights
+        // WARNING: Default value provided to shader if location available
+        float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, value, SHADER_ATTRIB_VEC4, 2);
+        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS);
+    }
+#endif
+
     if (mesh->indices != NULL)
     {
-        mesh->vboId[6] = rlLoadVertexBufferElement(mesh->indices, mesh->triangleCount*3*sizeof(unsigned short), dynamic);
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = rlLoadVertexBufferElement(mesh->indices, mesh->triangleCount*3*sizeof(unsigned short), dynamic);
     }
 
     if (mesh->vaoId > 0) TRACELOG(LOG_INFO, "VAO: [ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId);
@@ -1451,6 +1489,14 @@
 
     // Upload model normal matrix (if locations available)
     if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
+
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+    // Upload Bone Transforms    
+    if (material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1 && mesh.boneMatrices)
+    {
+        rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
+    }
+#endif
     //-----------------------------------------------------
 
     // Bind active texture maps (if available)
@@ -1478,19 +1524,19 @@
     if (!rlEnableVertexArray(mesh.vaoId))
     {
         // Bind mesh VBO data: vertex position (shader-location = 0)
-        rlEnableVertexBuffer(mesh.vboId[0]);
+        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION]);
         rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0);
         rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]);
 
         // Bind mesh VBO data: vertex texcoords (shader-location = 1)
-        rlEnableVertexBuffer(mesh.vboId[1]);
+        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD]);
         rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0);
         rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]);
 
         if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1)
         {
             // Bind mesh VBO data: vertex normals (shader-location = 2)
-            rlEnableVertexBuffer(mesh.vboId[2]);
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL]);
             rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0);
             rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]);
         }
@@ -1498,9 +1544,9 @@
         // Bind mesh VBO data: vertex colors (shader-location = 3, if available)
         if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1)
         {
-            if (mesh.vboId[3] != 0)
+            if (mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] != 0)
             {
-                rlEnableVertexBuffer(mesh.vboId[3]);
+                rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR]);
                 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0);
                 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
             }
@@ -1517,7 +1563,7 @@
         // Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
         if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1)
         {
-            rlEnableVertexBuffer(mesh.vboId[4]);
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT]);
             rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0);
             rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]);
         }
@@ -1525,12 +1571,30 @@
         // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
         if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1)
         {
-            rlEnableVertexBuffer(mesh.vboId[5]);
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2]);
             rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0);
             rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
         }
 
-        if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]);
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+        // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
+        }
+        
+        // Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]);
+        }
+#endif
+
+        if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]);
     }
 
     int eyeCount = 1;
@@ -1671,6 +1735,15 @@
 
     // Upload model normal matrix (if locations available)
     if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
+    
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+    // Upload Bone Transforms    
+    if (material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1 && mesh.boneMatrices)
+    {
+        rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
+    }
+#endif
+    
     //-----------------------------------------------------
 
     // Bind active texture maps (if available)
@@ -1696,19 +1769,19 @@
     if (!rlEnableVertexArray(mesh.vaoId))
     {
         // Bind mesh VBO data: vertex position (shader-location = 0)
-        rlEnableVertexBuffer(mesh.vboId[0]);
+        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION]);
         rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0);
         rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]);
 
         // Bind mesh VBO data: vertex texcoords (shader-location = 1)
-        rlEnableVertexBuffer(mesh.vboId[1]);
+        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD]);
         rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0);
         rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]);
 
         if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1)
         {
             // Bind mesh VBO data: vertex normals (shader-location = 2)
-            rlEnableVertexBuffer(mesh.vboId[2]);
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL]);
             rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0);
             rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]);
         }
@@ -1716,9 +1789,9 @@
         // Bind mesh VBO data: vertex colors (shader-location = 3, if available)
         if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1)
         {
-            if (mesh.vboId[3] != 0)
+            if (mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] != 0)
             {
-                rlEnableVertexBuffer(mesh.vboId[3]);
+                rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR]);
                 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0);
                 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
             }
@@ -1735,7 +1808,7 @@
         // Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
         if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1)
         {
-            rlEnableVertexBuffer(mesh.vboId[4]);
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT]);
             rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0);
             rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]);
         }
@@ -1743,12 +1816,30 @@
         // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
         if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1)
         {
-            rlEnableVertexBuffer(mesh.vboId[5]);
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2]);
             rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0);
             rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
         }
 
-        if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]);
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+        // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
+        }
+        
+        // Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]);
+        }
+#endif
+
+        if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]);
     }
 
     int eyeCount = 1;
@@ -1825,6 +1916,7 @@
     RL_FREE(mesh.animNormals);
     RL_FREE(mesh.boneWeights);
     RL_FREE(mesh.boneIds);
+    RL_FREE(mesh.boneMatrices);
 }
 
 // Export mesh data to file
@@ -2004,7 +2096,6 @@
     return success;
 }
 
-
 #if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
 // Process obj materials
 static void ProcessMaterialsOBJ(Material *materials, tinyobj_material_t *mats, int materialCount)
@@ -2016,6 +2107,8 @@
         // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE
         materials[m] = LoadMaterialDefault();
 
+        if (mats == NULL) continue;
+
         // Get default texture, in case no texture is defined
         // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
         materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
@@ -2253,6 +2346,50 @@
     }
 }
 
+void UpdateModelAnimationBoneMatrices(Model model, ModelAnimation anim, int frame)
+{
+    if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL))
+    {
+        if (frame >= anim.frameCount) frame = frame%anim.frameCount;    
+    
+        for (int i = 0; i < model.meshCount; i++)
+        {
+            if (model.meshes[i].boneMatrices)
+            {
+                assert(model.meshes[i].boneCount == anim.boneCount);
+              
+                for (int boneId = 0; boneId < model.meshes[i].boneCount; boneId++)
+                {
+                    Vector3 inTranslation = model.bindPose[boneId].translation;
+                    Quaternion inRotation = model.bindPose[boneId].rotation;
+                    Vector3 inScale = model.bindPose[boneId].scale;
+                    
+                    Vector3 outTranslation = anim.framePoses[frame][boneId].translation;
+                    Quaternion outRotation = anim.framePoses[frame][boneId].rotation;
+                    Vector3 outScale = anim.framePoses[frame][boneId].scale;
+
+                    Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation));
+                    Quaternion invRotation = QuaternionInvert(inRotation);
+                    Vector3 invScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, inScale);
+
+                    Vector3 boneTranslation = Vector3Add(
+                        Vector3RotateByQuaternion(Vector3Multiply(outScale, invTranslation),
+                        outRotation), outTranslation); 
+                    Quaternion boneRotation = QuaternionMultiply(outRotation, invRotation);
+                    Vector3 boneScale = Vector3Multiply(outScale, invScale);
+                    
+                    Matrix boneMatrix = MatrixMultiply(MatrixMultiply(
+                        QuaternionToMatrix(boneRotation),
+                        MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)),
+                        MatrixScale(boneScale.x, boneScale.y, boneScale.z));
+                    
+                    model.meshes[i].boneMatrices[boneId] = boneMatrix;
+                }
+            }
+        }
+    }
+}
+
 // Unload animation array data
 void UnloadModelAnimations(ModelAnimation *animations, int animCount)
 {
@@ -3631,6 +3768,30 @@
     rlDisableWireMode();
 }
 
+// Draw a model points
+void DrawModelPoints(Model model, Vector3 position, float scale, Color tint)
+{
+    rlEnablePointMode();
+    rlDisableBackfaceCulling();
+
+    DrawModel(model, position, scale, tint);
+
+    rlEnableBackfaceCulling();
+    rlDisableWireMode();
+}
+
+// Draw a model points
+void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
+{
+    rlEnablePointMode();
+    rlDisableBackfaceCulling();
+
+    DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
+
+    rlEnableBackfaceCulling();
+    rlDisableWireMode();
+}
+
 // Draw a billboard
 void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint)
 {
@@ -4049,126 +4210,238 @@
 //  - the mesh is automatically triangulated by tinyobj
 static Model LoadOBJ(const char *fileName)
 {
+    tinyobj_attrib_t objAttributes = { 0 };
+    tinyobj_shape_t* objShapes = NULL;
+    unsigned int objShapeCount = 0;
+
+    tinyobj_material_t* objMaterials = NULL;
+    unsigned int objMaterialCount = 0;
+
     Model model = { 0 };
+    model.transform = MatrixIdentity();
 
-    tinyobj_attrib_t attrib = { 0 };
-    tinyobj_shape_t *meshes = NULL;
-    unsigned int meshCount = 0;
+    char* fileText = LoadFileText(fileName);
 
-    tinyobj_material_t *materials = NULL;
-    unsigned int materialCount = 0;
+    if (fileText == NULL)
+    {
+        TRACELOG(LOG_ERROR, "MODEL Unable to read obj file %s", fileName);
+        return model;
+    }
 
-    char *fileText = LoadFileText(fileName);
+    char currentDir[1024] = { 0 };
+    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);
+    }
 
-    if (fileText != NULL)
+    unsigned int dataSize = (unsigned int)strlen(fileText);
+
+    unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
+    int ret = tinyobj_parse_obj(&objAttributes, &objShapes, &objShapeCount, &objMaterials, &objMaterialCount, fileText, dataSize, flags);
+
+    if (ret != TINYOBJ_SUCCESS)
     {
-        unsigned int dataSize = (unsigned int)strlen(fileText);
+        TRACELOG(LOG_ERROR, "MODEL Unable to read obj data %s", fileName);
+        return model;
+    }
 
-        char currentDir[1024] = { 0 };
-        strcpy(currentDir, GetWorkingDirectory()); // Save current working directory
-        const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness
-        if (CHDIR(workingDir) != 0)
+    UnloadFileText(fileText);
+
+    unsigned int faceVertIndex = 0;
+    unsigned int nextShape = 1;
+    int lastMaterial = -1;
+    unsigned int meshIndex = 0;
+
+    // count meshes
+    unsigned int nextShapeEnd = objAttributes.num_face_num_verts;
+
+    // see how many verts till the next shape
+
+    if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset;
+
+    // walk all the faces
+    for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
+    {
+        if (faceVertIndex >= nextShapeEnd)
         {
-            TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir);
+            // try to find the last vert in the next shape
+            nextShape++;
+            if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset;
+            else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces
+            meshIndex++;
         }
+        else if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial)
+        {
+            meshIndex++;// if this is a new material, we need to allocate a new mesh
+        }
 
-        unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
-        int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, fileText, dataSize, flags);
+        lastMaterial = objAttributes.material_ids[faceId];
+        faceVertIndex += objAttributes.face_num_verts[faceId];
+    }
 
-        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);
+    // allocate the base meshes and materials
+    model.meshCount = meshIndex + 1;
+    model.meshes = (Mesh*)MemAlloc(sizeof(Mesh) * model.meshCount);
 
-        // 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;
+    if (objMaterialCount > 0)
+    {
+        model.materialCount = objMaterialCount;
+        model.materials = (Material*)MemAlloc(sizeof(Material) * objMaterialCount);
+    }
+    else // we must allocate at least one material
+    {
+        model.materialCount = 1;
+        model.materials = (Material*)MemAlloc(sizeof(Material) * 1);
+    }
 
-        // 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.meshMaterial = (int*)MemAlloc(sizeof(int) * model.meshCount);
+
+    // see how many verts are in each mesh
+    unsigned int* localMeshVertexCounts = (unsigned int*)MemAlloc(sizeof(unsigned int) * model.meshCount);
+
+    faceVertIndex = 0;
+    nextShapeEnd = objAttributes.num_face_num_verts;
+    lastMaterial = -1;
+    meshIndex = 0;
+    unsigned int localMeshVertexCount = 0;
+
+    nextShape = 1;
+    if (objShapeCount > 1)
+        nextShapeEnd = objShapes[nextShape].face_offset;
+
+    // walk all the faces
+    for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
+    {
+        bool newMesh = false; // do we need a new mesh?
+        if (faceVertIndex >= nextShapeEnd)
         {
-            model.materialCount = 1;
-            TRACELOG(LOG_INFO, "MODEL: No materials provided, setting one default material for all meshes");
+            // try to find the last vert in the next shape
+            nextShape++;
+            if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset;
+            else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces
+
+            newMesh = true;
         }
+        else if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial)
+        {
+            newMesh = true;
+        }
 
-        // Init model meshes and materials
-        model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
-        model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); // Material index assigned to each mesh
-        model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
+        lastMaterial = objAttributes.material_ids[faceId];
 
-        // Process each provided mesh
-        for (int i = 0; i < model.meshCount; i++)
+        if (newMesh)
         {
-            // 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;
+            localMeshVertexCounts[meshIndex] = localMeshVertexCount;
 
-            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
+            localMeshVertexCount = 0;
+            meshIndex++;
+        }
 
-            // 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 += 3, vn += 3)
-            {
-                // 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];
+        faceVertIndex += objAttributes.face_num_verts[faceId];
+        localMeshVertexCount += objAttributes.face_num_verts[faceId];
+    }
+    localMeshVertexCounts[meshIndex] = localMeshVertexCount;
 
-                // 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]; }
+    for (int i = 0; i < model.meshCount; i++)
+    {
+        // allocate the buffers for each mesh
+        unsigned int vertexCount = localMeshVertexCounts[i];
 
-                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];
+        model.meshes[i].vertexCount = vertexCount;
+        model.meshes[i].triangleCount = vertexCount / 3;
 
-                    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];
+        model.meshes[i].vertices = (float*)MemAlloc(sizeof(float) * vertexCount * 3);
+        model.meshes[i].normals = (float*)MemAlloc(sizeof(float) * vertexCount * 3);
+        model.meshes[i].texcoords = (float*)MemAlloc(sizeof(float) * vertexCount * 2);
+        model.meshes[i].colors = (unsigned char*)MemAlloc(sizeof(unsigned char) * vertexCount * 4);
+    }
 
-                    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];
-                }
+    MemFree(localMeshVertexCounts);
+    localMeshVertexCounts = NULL;
 
-                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]; }
-                }
-            }
+    // fill meshes
+    faceVertIndex = 0;
+
+    nextShapeEnd = objAttributes.num_face_num_verts;
+
+    // see how many verts till the next shape
+    nextShape = 1;
+    if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset;
+    lastMaterial = -1;
+    meshIndex = 0;
+    localMeshVertexCount = 0;
+
+    // walk all the faces
+    for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
+    {
+        bool newMesh = false; // do we need a new mesh?
+        if (faceVertIndex >= nextShapeEnd)
+        {
+            // try to find the last vert in the next shape
+            nextShape++;
+            if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset;
+            else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces
+            newMesh = true;
         }
+        // if this is a new material, we need to allocate a new mesh
+        if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) newMesh = true;
+        lastMaterial = objAttributes.material_ids[faceId];
 
-        // Init model materials
-        if (materialCount > 0) ProcessMaterialsOBJ(model.materials, materials, materialCount);
-        else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh
+        if (newMesh)
+        {
+            localMeshVertexCount = 0;
+            meshIndex++;
+        }
 
-        tinyobj_attrib_free(&attrib);
-        tinyobj_shapes_free(meshes, model.meshCount);
-        tinyobj_materials_free(materials, materialCount);
+        int matId = 0;
+        if (lastMaterial >= 0 && lastMaterial < (int)objMaterialCount)
+            matId = lastMaterial;
 
-        UnloadFileText(fileText);
+        model.meshMaterial[meshIndex] = matId;
 
-        // Restore current working directory
-        if (CHDIR(currentDir) != 0)
+        for (int f = 0; f < objAttributes.face_num_verts[faceId]; f++)
         {
-            TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir);
+            int vertIndex = objAttributes.faces[faceVertIndex].v_idx;
+            int normalIndex = objAttributes.faces[faceVertIndex].vn_idx;
+            int texcordIndex = objAttributes.faces[faceVertIndex].vt_idx;
+
+            for (int i = 0; i < 3; i++)
+                model.meshes[meshIndex].vertices[localMeshVertexCount * 3 + i] = objAttributes.vertices[vertIndex * 3 + i];
+
+            for (int i = 0; i < 3; i++)
+                model.meshes[meshIndex].normals[localMeshVertexCount * 3 + i] = objAttributes.normals[normalIndex * 3 + i];
+
+            for (int i = 0; i < 2; i++)
+                model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + i] = objAttributes.texcoords[texcordIndex * 2 + i];
+
+            model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + 1];
+
+            for (int i = 0; i < 4; i++)
+                model.meshes[meshIndex].colors[localMeshVertexCount * 4 + i] = 255;
+
+            faceVertIndex++;
+            localMeshVertexCount++;
         }
     }
 
+    if (objMaterialCount > 0) ProcessMaterialsOBJ(model.materials, objMaterials, objMaterialCount);
+    else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh
+
+    tinyobj_attrib_free(&objAttributes);
+    tinyobj_shapes_free(objShapes, objShapeCount);
+    tinyobj_materials_free(objMaterials, objMaterialCount);
+
+    for (int i = 0; i < model.meshCount; i++)
+        UploadMesh(model.meshes + i, true);
+
+    // Restore current working directory
+    if (CHDIR(currentDir) != 0)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir);
+    }
+
     return model;
 }
 #endif
@@ -4533,6 +4806,17 @@
     }
 
     BuildPoseFromParentJoints(model.bones, model.boneCount, model.bindPose);
+    
+    for (int i = 0; i < model.meshCount; i++)
+    {
+        model.meshes[i].boneCount = model.boneCount;
+        model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
+        
+        for (int j = 0; j < model.meshes[i].boneCount; j++)
+        {
+            model.meshes[i].boneMatrices[j] = MatrixIdentity();
+        }
+    }
 
     UnloadFileData(fileData);
 
@@ -4942,17 +5226,19 @@
     ***********************************************************************************************/
 
     // Macro to simplify attributes loading code
-    #define LOAD_ATTRIBUTE(accesor, numComp, dataType, dstPtr) \
+    #define LOAD_ATTRIBUTE(accesor, numComp, srcType, dstPtr) LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, srcType) 
+
+    #define LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, dstType) \
     { \
         int n = 0; \
-        dataType *buffer = (dataType *)accesor->buffer_view->buffer->data + accesor->buffer_view->offset/sizeof(dataType) + accesor->offset/sizeof(dataType); \
+        srcType *buffer = (srcType *)accesor->buffer_view->buffer->data + accesor->buffer_view->offset/sizeof(srcType) + accesor->offset/sizeof(srcType); \
         for (unsigned int k = 0; k < accesor->count; k++) \
         {\
             for (int l = 0; l < numComp; l++) \
             {\
-                dstPtr[numComp*k + l] = buffer[n + l];\
+                dstPtr[numComp*k + l] = (dstType)buffer[n + l];\
             }\
-            n += (int)(accesor->stride/sizeof(dataType));\
+            n += (int)(accesor->stride/sizeof(srcType));\
         }\
     }
 
@@ -5393,8 +5679,6 @@
                             else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName);
                         }
                         else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName);
-
-
                     }
 
                     // NOTE: Attributes related to animations are processed separately
@@ -5415,23 +5699,25 @@
                         // Load unsigned short data type into mesh.indices
                         LOAD_ATTRIBUTE(attribute, 1, unsigned short, model.meshes[meshIndex].indices)
                     }
+                    else if (attribute->component_type == cgltf_component_type_r_8u)
+                    {
+                        // Init raylib mesh indices to copy glTF attribute data
+                        model.meshes[meshIndex].indices = RL_MALLOC(attribute->count * sizeof(unsigned short));
+                        LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned char, model.meshes[meshIndex].indices, unsigned short)
+
+                    }
                     else if (attribute->component_type == cgltf_component_type_r_32u)
                     {
                         // Init raylib mesh indices to copy glTF attribute data
                         model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short));
-
-                        // Load data into a temp buffer to be converted to raylib data type
-                        unsigned int *temp = RL_MALLOC(attribute->count*sizeof(unsigned int));
-                        LOAD_ATTRIBUTE(attribute, 1, unsigned int, temp);
-
-                        // Convert data to raylib indices data type (unsigned short)
-                        for (unsigned int d = 0; d < attribute->count; d++) model.meshes[meshIndex].indices[d] = (unsigned short)temp[d];
+                        LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned int, model.meshes[meshIndex].indices, unsigned short);
 
                         TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data converted from u32 to u16, possible loss of data", fileName);
-
-                        RL_FREE(temp);
                     }
-                    else TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data format not supported, use u16", fileName);
+                    else 
+                    {
+                        TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data format not supported, use u16", fileName);
+                    }
                 }
                 else model.meshes[meshIndex].triangleCount = model.meshes[meshIndex].vertexCount/3;    // Unindexed mesh
 
@@ -5463,7 +5749,7 @@
         //  - 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, weights not considered (i.e. morph targets)
         //----------------------------------------------------------------------------------------------------
-        if (data->skins_count == 1)
+        if (data->skins_count > 0)
         {
             cgltf_skin skin = data->skins[0];
             model.bones = LoadBoneInfoGLTF(skin, &model.boneCount);
@@ -5483,9 +5769,9 @@
                 MatrixDecompose(worldMatrix, &(model.bindPose[i].translation), &(model.bindPose[i].rotation), &(model.bindPose[i].scale));
             }
         }
-        else if (data->skins_count > 1)
+        if (data->skins_count > 1)
         {
-            TRACELOG(LOG_ERROR, "MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i", fileName, data->skins_count);
+            TRACELOG(LOG_WARNING, "MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i", fileName, data->skins_count);
         }
 
         meshIndex = 0;
@@ -5616,6 +5902,15 @@
                 {
                     memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float));
                 }
+                
+                // Bone Transform Matrices
+                model.meshes[meshIndex].boneCount = model.boneCount;
+                model.meshes[meshIndex].boneMatrices = RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix));
+                
+                for (int j = 0; j < model.meshes[meshIndex].boneCount; j++)
+                {
+                    model.meshes[meshIndex].boneMatrices[j] = MatrixIdentity();
+                }
 
                 meshIndex++;       // Move to next mesh
             }
@@ -5741,11 +6036,11 @@
                 cgltf_accessor_read_float(output, 3*keyframe+1, tmp, 4);
                 Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]};
                 cgltf_accessor_read_float(output, 3*keyframe+2, tmp, 4);
-                Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2]};
+                Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2], 0.0f};
                 cgltf_accessor_read_float(output, 3*(keyframe+1)+1, tmp, 4);
                 Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]};
                 cgltf_accessor_read_float(output, 3*(keyframe+1), tmp, 4);
-                Vector4 inTangent2 = {tmp[0], tmp[1], tmp[2]};
+                Vector4 inTangent2 = {tmp[0], tmp[1], tmp[2], 0.0f};
                 Vector4 *r = data;
 
                 v1 = QuaternionNormalize(v1);
@@ -5797,7 +6092,7 @@
 
     if (result == cgltf_result_success)
     {
-        if (data->skins_count == 1)
+        if (data->skins_count > 0)
         {
             cgltf_skin skin = data->skins[0];
             *animCount = (int)data->animations_count;
@@ -5932,7 +6227,11 @@
                 RL_FREE(boneChannels);
             }
         }
-        else TRACELOG(LOG_ERROR, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count);
+        
+        if (data->skins_count > 1)
+        {
+            TRACELOG(LOG_WARNING, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count);
+        }
 
         cgltf_free(data);
     }
@@ -6384,6 +6683,13 @@
             {
                 memcpy(model.meshes[i].animVertices, model.meshes[i].vertices, model.meshes[i].vertexCount*3*sizeof(float));
                 memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float));
+                
+                model.meshes[i].boneCount = model.boneCount;
+                model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
+                for (j = 0; j < model.meshes[i].boneCount; j++)
+                {
+                    model.meshes[i].boneMatrices[j] = MatrixIdentity();
+                }
             }
         }
 
diff --git a/raylib/src/rshapes.c b/raylib/src/rshapes.c
--- a/raylib/src/rshapes.c
+++ b/raylib/src/rshapes.c
@@ -79,8 +79,8 @@
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-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
+static Texture2D texShapes = { 1, 1, 1, 1, 7 };                // Texture used on shapes drawing (white pixel loaded by rlgl)
+static Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f };    // Texture source rectangle used on shapes drawing
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
@@ -430,17 +430,16 @@
 }
 
 // Draw a gradient-filled circle
-// NOTE: Gradient goes from center (color1) to border (color2)
-void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2)
+void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer)
 {
     rlBegin(RL_TRIANGLES);
         for (int i = 0; i < 360; i += 10)
         {
-            rlColor4ub(color1.r, color1.g, color1.b, color1.a);
+            rlColor4ub(inner.r, inner.g, inner.b, inner.a);
             rlVertex2f((float)centerX, (float)centerY);
-            rlColor4ub(color2.r, color2.g, color2.b, color2.a);
+            rlColor4ub(outer.r, outer.g, outer.b, outer.a);
             rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radius, (float)centerY + sinf(DEG2RAD*(i + 10))*radius);
-            rlColor4ub(color2.r, color2.g, color2.b, color2.a);
+            rlColor4ub(outer.r, outer.g, outer.b, outer.a);
             rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radius, (float)centerY + sinf(DEG2RAD*i)*radius);
         }
     rlEnd();
@@ -761,22 +760,19 @@
 }
 
 // Draw a vertical-gradient-filled rectangle
-// NOTE: Gradient goes from bottom (color1) to top (color2)
-void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2)
+void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom)
 {
-    DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color1, color2, color2, color1);
+    DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, top, bottom, bottom, top);
 }
 
 // Draw a horizontal-gradient-filled rectangle
-// NOTE: Gradient goes from bottom (color1) to top (color2)
-void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2)
+void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right)
 {
-    DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color1, color1, color2, color2);
+    DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, left, left, right, right);
 }
 
 // Draw a gradient-filled rectangle
-// NOTE: Colors refer to corners, starting at top-lef corner and counter-clockwise
-void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4)
+void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight)
 {
     rlSetTexture(GetShapesTexture().id);
     Rectangle shapeRect = GetShapesTextureRectangle();
@@ -785,19 +781,19 @@
         rlNormal3f(0.0f, 0.0f, 1.0f);
 
         // NOTE: Default raylib font character 95 is a white square
-        rlColor4ub(col1.r, col1.g, col1.b, col1.a);
+        rlColor4ub(topLeft.r, topLeft.g, topLeft.b, topLeft.a);
         rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
         rlVertex2f(rec.x, rec.y);
 
-        rlColor4ub(col2.r, col2.g, col2.b, col2.a);
+        rlColor4ub(bottomLeft.r, bottomLeft.g, bottomLeft.b, bottomLeft.a);
         rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
         rlVertex2f(rec.x, rec.y + rec.height);
 
-        rlColor4ub(col3.r, col3.g, col3.b, col3.a);
+        rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a);
         rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
         rlVertex2f(rec.x + rec.width, rec.y + rec.height);
 
-        rlColor4ub(col4.r, col4.g, col4.b, col4.a);
+        rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a);
         rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
         rlVertex2f(rec.x + rec.width, rec.y);
     rlEnd();
diff --git a/raylib/src/rtext.c b/raylib/src/rtext.c
--- a/raylib/src/rtext.c
+++ b/raylib/src/rtext.c
@@ -124,6 +124,7 @@
 //----------------------------------------------------------------------------------
 // Global variables
 //----------------------------------------------------------------------------------
+extern bool isGpuReady;
 #if defined(SUPPORT_DEFAULT_FONT)
 // Default font provided by raylib
 // NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core]
@@ -252,15 +253,15 @@
         counter++;
     }
 
-    defaultFont.texture = LoadTextureFromImage(imFont);
+    if (isGpuReady) defaultFont.texture = LoadTextureFromImage(imFont);
 
     // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, glyphCount
     //------------------------------------------------------------------------------
 
     // Allocate space for our characters info data
     // NOTE: This memory must be freed at end! --> Done by CloseWindow()
-    defaultFont.glyphs = (GlyphInfo *)RL_MALLOC(defaultFont.glyphCount*sizeof(GlyphInfo));
-    defaultFont.recs = (Rectangle *)RL_MALLOC(defaultFont.glyphCount*sizeof(Rectangle));
+    defaultFont.glyphs = (GlyphInfo *)RL_CALLOC(defaultFont.glyphCount, sizeof(GlyphInfo));
+    defaultFont.recs = (Rectangle *)RL_CALLOC(defaultFont.glyphCount, sizeof(Rectangle));
 
     int currentLine = 0;
     int currentPosX = charsDivisor;
@@ -308,7 +309,7 @@
 extern void UnloadFontDefault(void)
 {
     for (int i = 0; i < defaultFont.glyphCount; i++) UnloadImage(defaultFont.glyphs[i].image);
-    UnloadTexture(defaultFont.texture);
+    if (isGpuReady) UnloadTexture(defaultFont.texture);
     RL_FREE(defaultFont.glyphs);
     RL_FREE(defaultFont.recs);
 }
@@ -362,11 +363,14 @@
         UnloadImage(image);
     }
 
-    if (font.texture.id == 0) TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
-    else
+    if (isGpuReady)
     {
-        SetTextureFilter(font.texture, TEXTURE_FILTER_POINT);    // By default, we set point filter (the best performance)
-        TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", FONT_TTF_DEFAULT_SIZE, FONT_TTF_DEFAULT_NUMCHARS);
+        if (font.texture.id == 0) TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
+        else
+        {
+            SetTextureFilter(font.texture, TEXTURE_FILTER_POINT);    // By default, we set point filter (the best performance)
+            TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", FONT_TTF_DEFAULT_SIZE, FONT_TTF_DEFAULT_NUMCHARS);
+        }
     }
 
     return font;
@@ -487,7 +491,7 @@
     };
 
     // Set font with all data parsed from image
-    font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
+    if (isGpuReady) font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
     font.glyphCount = index;
     font.glyphPadding = 0;
 
@@ -556,7 +560,7 @@
         font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
 
         Image atlas = GenImageFontAtlas(font.glyphs, &font.recs, font.glyphCount, font.baseSize, font.glyphPadding, 0);
-        font.texture = LoadTextureFromImage(atlas);
+        if (isGpuReady) font.texture = LoadTextureFromImage(atlas);
 
         // Update glyphs[i].image to use alpha, required to be used on ImageDrawText()
         for (int i = 0; i < font.glyphCount; i++)
@@ -580,7 +584,7 @@
 // Check if a font is ready
 bool IsFontReady(Font font)
 {
-    return ((font.texture.id > 0) &&    // Validate OpenGL id fot font texture atlas
+    return ((font.texture.id > 0) &&    // Validate OpenGL id for font texture atlas
             (font.baseSize > 0) &&      // Validate font size
             (font.glyphCount > 0) &&    // Validate font contains some glyph
             (font.recs != NULL) &&      // Validate font recs defining glyphs on texture atlas
@@ -673,6 +677,8 @@
                     {
                         stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
                         chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor);
+                        
+                        if (chh > fontSize) TRACELOG(LOG_WARNING, "FONT: Character [0x%08x] size is bigger than expected font size", ch);
 
                         // Load characters images
                         chars[i].image.width = chw;
@@ -946,7 +952,7 @@
     if (font.texture.id != GetFontDefault().texture.id)
     {
         UnloadFontData(font.glyphs, font.glyphCount);
-        UnloadTexture(font.texture);
+        if (isGpuReady) UnloadTexture(font.texture);
         RL_FREE(font.recs);
 
         TRACELOGD("FONT: Unloaded font data from RAM and VRAM");
@@ -1066,7 +1072,7 @@
     byteCount += sprintf(txtData + byteCount, "    Image imFont = { fontImageData_%s, %i, %i, 1, %i };\n\n", styleName, image.width, image.height, image.format);
 #endif
     byteCount += sprintf(txtData + byteCount, "    // Load texture from image\n");
-    byteCount += sprintf(txtData + byteCount, "    font.texture = LoadTextureFromImage(imFont);\n");
+    byteCount += sprintf(txtData + byteCount, "    if (isGpuReady) font.texture = LoadTextureFromImage(imFont);\n");
 #if defined(SUPPORT_COMPRESSED_FONT_ATLAS)
     byteCount += sprintf(txtData + byteCount, "    UnloadImage(imFont);  // Uncompressed data can be unloaded from memory\n\n");
 #endif
@@ -1277,7 +1283,7 @@
 {
     Vector2 textSize = { 0 };
 
-    if ((font.texture.id == 0) || (text == NULL)) return textSize; // Security check
+    if ((isGpuReady && (font.texture.id == 0)) || (text == NULL)) return textSize; // Security check
 
     int size = TextLength(text);    // Get size in bytes of text
     int tempByteCounter = 0;        // Used to count longer text line num chars
@@ -1467,8 +1473,7 @@
     int i = 0;
     for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
 
-    if (text[i++] != '.') value *= sign;
-    else
+    if (text[i++] == '.')
     {
         float divisor = 10.0f;
         for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
@@ -1478,7 +1483,7 @@
         }
     }
 
-    return value;
+    return value*sign;
 }
 
 #if defined(SUPPORT_TEXT_MANIPULATION)
@@ -2258,7 +2263,7 @@
 
     RL_FREE(imFonts);
 
-    font.texture = LoadTextureFromImage(fullFont);
+    if (isGpuReady) font.texture = LoadTextureFromImage(fullFont);
 
     // Fill font characters info data
     font.baseSize = fontSize;
@@ -2300,7 +2305,7 @@
     UnloadImage(fullFont);
     UnloadFileText(fileText);
 
-    if (font.texture.id == 0)
+    if (isGpuReady && (font.texture.id == 0))
     {
         UnloadFont(font);
         font = GetFontDefault();
diff --git a/raylib/src/rtextures.c b/raylib/src/rtextures.c
--- a/raylib/src/rtextures.c
+++ b/raylib/src/rtextures.c
@@ -225,14 +225,6 @@
     #pragma GCC diagnostic pop
 #endif
 
-#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
 //----------------------------------------------------------------------------------
@@ -335,84 +327,6 @@
     return image;
 }
 
-// Load an image from a SVG file or string with custom size
-Image LoadImageSvg(const char *fileNameOrString, int width, int height)
-{
-    Image image = { 0 };
-
-#if defined(SUPPORT_FILEFORMAT_SVG)
-    bool isSvgStringValid = false;
-
-    // Validate fileName or string
-    if (fileNameOrString != NULL)
-    {
-        int dataSize = 0;
-        unsigned char *fileData = NULL;
-
-        if (FileExists(fileNameOrString))
-        {
-            fileData = LoadFileData(fileNameOrString, &dataSize);
-            isSvgStringValid = true;
-        }
-        else
-        {
-            // Validate fileData as valid SVG string data
-            //<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2484" viewBox="0 0 192.756 191.488">
-            if ((fileNameOrString != NULL) &&
-                (fileNameOrString[0] == '<') &&
-                (fileNameOrString[1] == 's') &&
-                (fileNameOrString[2] == 'v') &&
-                (fileNameOrString[3] == 'g'))
-            {
-                fileData = (unsigned char *)fileNameOrString;
-                isSvgStringValid = true;
-            }
-        }
-
-        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
-            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;
-
-            // Free used memory
-            nsvgDelete(svgImage);
-            nsvgDeleteRasterizer(rast);
-        }
-
-        if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData);
-    }
-#else
-    TRACELOG(LOG_WARNING, "SVG image support not enabled, image can not be loaded");
-#endif
-
-    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
@@ -503,7 +417,16 @@
     Image image = { 0 };
 
     // Security check for input data
-    if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image;
+    if ((fileData == NULL) || (dataSize == 0))
+    {
+        TRACELOG(LOG_WARNING, "IMAGE: Invalid file data");
+        return image;
+    }
+    if (fileType == NULL)
+    {
+        TRACELOG(LOG_WARNING, "IMAGE: Missing file extension");
+        return image;
+    }
 
     if ((false)
 #if defined(SUPPORT_FILEFORMAT_PNG)
@@ -591,36 +514,6 @@
         }
     }
 #endif
-#if defined(SUPPORT_FILEFORMAT_SVG)
-    else if ((strcmp(fileType, ".svg") == 0) || (strcmp(fileType, ".SVG") == 0))
-    {
-        // Validate fileData as valid SVG string data
-        //<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2484" viewBox="0 0 192.756 191.488">
-        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);
-
-            // 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);
-            nsvgDeleteRasterizer(rast);
-        }
-    }
-#endif
 #if defined(SUPPORT_FILEFORMAT_DDS)
     else if ((strcmp(fileType, ".dds") == 0) || (strcmp(fileType, ".DDS") == 0))
     {
@@ -1103,6 +996,7 @@
 {
     Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
 
+    float aspectRatio = (float)width / (float)height;
     for (int y = 0; y < height; y++)
     {
         for (int x = 0; x < width; x++)
@@ -1110,6 +1004,10 @@
             float nx = (float)(x + offsetX)*(scale/(float)width);
             float ny = (float)(y + offsetY)*(scale/(float)height);
 
+            // Apply aspect ratio compensation to wider side
+            if (width > height) nx *= aspectRatio;
+            else ny /= aspectRatio;
+            
             // Basic perlin noise implementation (not used)
             //float p = (stb_perlin_noise3(nx, ny, 0.0f, 0, 0, 0);
 
@@ -1213,6 +1111,7 @@
 {
     Image image = { 0 };
 
+#if defined(SUPPORT_MODULE_RTEXT)
     int textLength = TextLength(text);
     int imageViewSize = width*height;
 
@@ -1223,6 +1122,10 @@
     image.mipmaps = 1;
 
     memcpy(image.data, text, (textLength > imageViewSize)? imageViewSize : textLength);
+#else
+    TRACELOG(LOG_WARNING, "IMAGE: GenImageText() requires module: rtext");
+    image = GenImageColor(width, height, BLACK);     // Generating placeholder black image rectangle
+#endif
 
     return image;
 }
@@ -4546,6 +4449,9 @@
 
         if (source.width < 0) { flipX = true; source.width *= -1; }
         if (source.height < 0) source.y -= source.height;
+        
+        if (dest.width < 0) dest.width *= -1;
+        if (dest.height < 0) dest.height *= -1;
 
         Vector2 topLeft = { 0 };
         Vector2 topRight = { 0 };
@@ -5164,6 +5070,22 @@
     return out;
 }
 
+// Get color lerp interpolation between two colors, factor [0.0f..1.0f]
+Color ColorLerp(Color color1, Color color2, float factor) 
+{ 
+    Color color = { 0 };
+    
+    if (factor < 0.0f) factor = 0.0f;
+    else if (factor > 1.0f) factor = 1.0f;
+
+    color.r = (unsigned char)((1.0f - factor)*color1.r + factor*color2.r);
+    color.g = (unsigned char)((1.0f - factor)*color1.g + factor*color2.g);
+    color.b = (unsigned char)((1.0f - factor)*color1.b + factor*color2.b);
+    color.a = (unsigned char)((1.0f - factor)*color1.a + factor*color2.a);
+
+    return color;
+}
+
 // Get a Color struct from hexadecimal value
 Color GetColor(unsigned int hexValue)
 {
@@ -5389,7 +5311,8 @@
         default: break;
     }
 
-    dataSize = width*height*bpp/8;  // Total data size in bytes
+    double bytesPerPixel = (double)bpp/8.0;
+    dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
 
     // Most compressed formats works on 4x4 blocks,
     // if texture is smaller, minimum dataSize is 8 or 16
diff --git a/raylib/src/utils.c b/raylib/src/utils.c
--- a/raylib/src/utils.c
+++ b/raylib/src/utils.c
@@ -76,7 +76,6 @@
 void SetLoadFileTextCallback(LoadFileTextCallback callback) { loadFileText = callback; }  // Set custom file text loader
 void SetSaveFileTextCallback(SaveFileTextCallback callback) { saveFileText = callback; }  // Set custom file text saver
 
-
 #if defined(PLATFORM_ANDROID)
 static AAssetManager *assetManager = NULL;          // Android assets manager pointer
 static const char *internalDataPath = NULL;         // Android internal data path
diff --git a/src/Raylib/Core.hs b/src/Raylib/Core.hs
--- a/src/Raylib/Core.hs
+++ b/src/Raylib/Core.hs
@@ -112,6 +112,7 @@
     traceLog,
     setTraceLogLevel,
     openURL,
+    setTraceLogCallback,
     setLoadFileDataCallback,
     setSaveFileDataCallback,
     setLoadFileTextCallback,
@@ -132,6 +133,7 @@
     getPrevDirectoryPath,
     getWorkingDirectory,
     getApplicationDirectory,
+    makeDirectory,
     changeDirectory,
     isPathFile,
     isFileNameValid,
@@ -333,6 +335,7 @@
     c'getPrevDirectoryPath,
     c'getWorkingDirectory,
     c'getApplicationDirectory,
+    c'makeDirectory,
     c'changeDirectory,
     c'isPathFile,
     c'isFileNameValid,
@@ -482,209 +485,211 @@
     VrDeviceInfo,
     VrStereoConfig,
     unpackShaderUniformData,
-    unpackShaderUniformDataV,
+    unpackShaderUniformDataV, C'TraceLogCallback, TraceLogCallback,
   )
 import GHC.IO (unsafePerformIO)
 
 $( genNative
-     [ ("c'initWindow", "InitWindow_", "rl_bindings.h", [t|CInt -> CInt -> CString -> IO ()|], False),
-       ("c'windowShouldClose", "WindowShouldClose_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'closeWindow", "CloseWindow_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'isWindowReady", "IsWindowReady_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'isWindowFullscreen", "IsWindowFullscreen_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'isWindowHidden", "IsWindowHidden_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'isWindowMinimized", "IsWindowMinimized_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'isWindowMaximized", "IsWindowMaximized_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'isWindowFocused", "IsWindowFocused_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'isWindowResized", "IsWindowResized_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'isWindowState", "IsWindowState_", "rl_bindings.h", [t|CUInt -> IO CBool|], False),
-       ("c'setWindowState", "SetWindowState_", "rl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'clearWindowState", "ClearWindowState_", "rl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'toggleFullscreen", "ToggleFullscreen_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'toggleBorderlessWindowed", "ToggleBorderlessWindowed_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'maximizeWindow", "MaximizeWindow_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'minimizeWindow", "MinimizeWindow_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'restoreWindow", "RestoreWindow_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'setWindowIcon", "SetWindowIcon_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'setWindowIcons", "SetWindowIcons_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|], False),
-       ("c'setWindowTitle", "SetWindowTitle_", "rl_bindings.h", [t|CString -> IO ()|], False),
-       ("c'setWindowPosition", "SetWindowPosition_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'setWindowMonitor", "SetWindowMonitor_", "rl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'setWindowMinSize", "SetWindowMinSize_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'setWindowMaxSize", "SetWindowMaxSize_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'setWindowSize", "SetWindowSize_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'setWindowOpacity", "SetWindowOpacity_", "rl_bindings.h", [t|CFloat -> IO ()|], False),
-       ("c'setWindowFocused", "SetWindowFocused_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'getWindowHandle", "GetWindowHandle_", "rl_bindings.h", [t|IO (Ptr ())|], False),
-       ("c'getScreenWidth", "GetScreenWidth_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getScreenHeight", "GetScreenHeight_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getRenderWidth", "GetRenderWidth_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getRenderHeight", "GetRenderHeight_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getMonitorCount", "GetMonitorCount_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getCurrentMonitor", "GetCurrentMonitor_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getMonitorPosition", "GetMonitorPosition_", "rl_bindings.h", [t|CInt -> IO (Ptr Vector2)|], False),
-       ("c'getMonitorWidth", "GetMonitorWidth_", "rl_bindings.h", [t|CInt -> IO CInt|], False),
-       ("c'getMonitorHeight", "GetMonitorHeight_", "rl_bindings.h", [t|CInt -> IO CInt|], False),
-       ("c'getMonitorPhysicalWidth", "GetMonitorPhysicalWidth_", "rl_bindings.h", [t|CInt -> IO CInt|], False),
-       ("c'getMonitorPhysicalHeight", "GetMonitorPhysicalHeight_", "rl_bindings.h", [t|CInt -> IO CInt|], False),
-       ("c'getMonitorRefreshRate", "GetMonitorRefreshRate_", "rl_bindings.h", [t|CInt -> IO CInt|], False),
-       ("c'getWindowPosition", "GetWindowPosition_", "rl_bindings.h", [t|IO (Ptr Vector2)|], False),
-       ("c'getWindowScaleDPI", "GetWindowScaleDPI_", "rl_bindings.h", [t|IO (Ptr Vector2)|], False),
-       ("c'getMonitorName", "GetMonitorName_", "rl_bindings.h", [t|CInt -> IO CString|], False),
-       ("c'setClipboardText", "SetClipboardText_", "rl_bindings.h", [t|CString -> IO ()|], False),
-       ("c'getClipboardText", "GetClipboardText_", "rl_bindings.h", [t|IO CString|], False),
-       ("c'enableEventWaiting", "EnableEventWaiting_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'disableEventWaiting", "DisableEventWaiting_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'swapScreenBuffer", "SwapScreenBuffer_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'pollInputEvents", "PollInputEvents_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'waitTime", "WaitTime_", "rl_bindings.h", [t|CDouble -> IO ()|], False),
-       ("c'showCursor", "ShowCursor_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'hideCursor", "HideCursor_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'isCursorHidden", "IsCursorHidden_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'enableCursor", "EnableCursor_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'disableCursor", "DisableCursor_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'isCursorOnScreen", "IsCursorOnScreen_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'clearBackground", "ClearBackground_", "rl_bindings.h", [t|Ptr Color -> IO ()|], False),
-       ("c'beginDrawing", "BeginDrawing_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'endDrawing", "EndDrawing_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'beginMode2D", "BeginMode2D_", "rl_bindings.h", [t|Ptr Camera2D -> IO ()|], False),
-       ("c'endMode2D", "EndMode2D_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'beginMode3D", "BeginMode3D_", "rl_bindings.h", [t|Ptr Camera3D -> IO ()|], False),
-       ("c'endMode3D", "EndMode3D_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'beginTextureMode", "BeginTextureMode_", "rl_bindings.h", [t|Ptr RenderTexture -> IO ()|], False),
-       ("c'endTextureMode", "EndTextureMode_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'beginShaderMode", "BeginShaderMode_", "rl_bindings.h", [t|Ptr Shader -> IO ()|], False),
-       ("c'endShaderMode", "EndShaderMode_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'beginBlendMode", "BeginBlendMode_", "rl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'endBlendMode", "EndBlendMode_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'beginScissorMode", "BeginScissorMode_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'endScissorMode", "EndScissorMode_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'beginVrStereoMode", "BeginVrStereoMode_", "rl_bindings.h", [t|Ptr VrStereoConfig -> IO ()|], False),
-       ("c'endVrStereoMode", "EndVrStereoMode_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'loadVrStereoConfig", "LoadVrStereoConfig_", "rl_bindings.h", [t|Ptr VrDeviceInfo -> IO (Ptr VrStereoConfig)|], False),
-       ("c'unloadVrStereoConfig", "UnloadVrStereoConfig_", "rl_bindings.h", [t|Ptr VrStereoConfig -> IO ()|], False),
-       ("c'loadShader", "LoadShader_", "rl_bindings.h", [t|CString -> CString -> IO (Ptr Shader)|], False),
-       ("c'loadShaderFromMemory", "LoadShaderFromMemory_", "rl_bindings.h", [t|CString -> CString -> IO (Ptr Shader)|], False),
-       ("c'isShaderReady", "IsShaderReady_", "rl_bindings.h", [t|Ptr Shader -> IO CBool|], False),
-       ("c'getShaderLocation", "GetShaderLocation_", "rl_bindings.h", [t|Ptr Shader -> CString -> IO CInt|], False),
-       ("c'getShaderLocationAttrib", "GetShaderLocationAttrib_", "rl_bindings.h", [t|Ptr Shader -> CString -> IO CInt|], False),
-       ("c'setShaderValue", "SetShaderValue_", "rl_bindings.h", [t|Ptr Shader -> CInt -> Ptr () -> CInt -> IO ()|], False),
-       ("c'setShaderValueV", "SetShaderValueV_", "rl_bindings.h", [t|Ptr Shader -> CInt -> Ptr () -> CInt -> CInt -> IO ()|], False),
-       ("c'setShaderValueMatrix", "SetShaderValueMatrix_", "rl_bindings.h", [t|Ptr Shader -> CInt -> Ptr Matrix -> IO ()|], False),
-       ("c'setShaderValueTexture", "SetShaderValueTexture_", "rl_bindings.h", [t|Ptr Shader -> CInt -> Ptr Texture -> IO ()|], False),
-       ("c'unloadShader", "UnloadShader_", "rl_bindings.h", [t|Ptr Shader -> IO ()|], False),
-       ("c'getScreenToWorldRay", "GetScreenToWorldRay_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Camera3D -> IO (Ptr Ray)|], False),
-       ("c'getScreenToWorldRayEx", "GetScreenToWorldRayEx_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Camera3D -> CFloat -> CFloat -> IO (Ptr Ray)|], False),
-       ("c'getCameraMatrix", "GetCameraMatrix_", "rl_bindings.h", [t|Ptr Camera3D -> IO (Ptr Matrix)|], False),
-       ("c'getCameraMatrix2D", "GetCameraMatrix2D_", "rl_bindings.h", [t|Ptr Camera2D -> IO (Ptr Matrix)|], False),
-       ("c'getWorldToScreen", "GetWorldToScreen_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Camera3D -> IO (Ptr Vector2)|], False),
-       ("c'getScreenToWorld2D", "GetScreenToWorld2D_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Camera2D -> IO (Ptr Vector2)|], False),
-       ("c'getWorldToScreenEx", "GetWorldToScreenEx_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Camera3D -> CInt -> CInt -> IO (Ptr Vector2)|], False),
-       ("c'getWorldToScreen2D", "GetWorldToScreen2D_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Camera2D -> IO (Ptr Vector2)|], False),
-       ("c'setTargetFPS", "SetTargetFPS_", "rl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'getFPS", "GetFPS_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getFrameTime", "GetFrameTime_", "rl_bindings.h", [t|IO CFloat|], False),
-       ("c'getTime", "GetTime_", "rl_bindings.h", [t|IO CDouble|], False),
-       ("c'setRandomSeed", "SetRandomSeed_", "rl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'getRandomValue", "GetRandomValue_", "rl_bindings.h", [t|CInt -> CInt -> IO CInt|], False),
-       ("c'loadRandomSequence", "LoadRandomSequence_", "rl_bindings.h", [t|CUInt -> CInt -> CInt -> IO (Ptr CInt)|], False),
-       ("c'takeScreenshot", "TakeScreenshot_", "rl_bindings.h", [t|CString -> IO ()|], False),
-       ("c'setConfigFlags", "SetConfigFlags_", "rl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'traceLog", "TraceLog_", "rl_bindings.h", [t|CInt -> CString -> IO ()|], False), -- Uses varags, can't implement complete functionality
-       ("c'setTraceLogLevel", "SetTraceLogLevel_", "rl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'memAlloc", "MemAlloc_", "rl_bindings.h", [t|CInt -> IO (Ptr ())|], False),
-       ("c'memRealloc", "MemRealloc_", "rl_bindings.h", [t|Ptr () -> CInt -> IO (Ptr ())|], False),
-       ("c'memFree", "MemFree_", "rl_bindings.h", [t|Ptr () -> IO ()|], False),
-       ("c'openURL", "OpenURL_", "rl_bindings.h", [t|CString -> IO ()|], False),
-       ("c'setLoadFileDataCallback", "SetLoadFileDataCallback_", "rl_bindings.h", [t|C'LoadFileDataCallback -> IO ()|], False),
-       ("c'setSaveFileDataCallback", "SetSaveFileDataCallback_", "rl_bindings.h", [t|C'SaveFileDataCallback -> IO ()|], False),
-       ("c'setLoadFileTextCallback", "SetLoadFileTextCallback_", "rl_bindings.h", [t|C'LoadFileTextCallback -> IO ()|], False),
-       ("c'setSaveFileTextCallback", "SetSaveFileTextCallback_", "rl_bindings.h", [t|C'SaveFileTextCallback -> IO ()|], False),
-       ("c'loadFileData", "LoadFileData_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr CUChar)|], True),
-       ("c'unloadFileData", "UnloadFileData_", "rl_bindings.h", [t|Ptr CUChar -> IO ()|], False),
-       ("c'saveFileData", "SaveFileData_", "rl_bindings.h", [t|CString -> Ptr () -> CInt -> IO CBool|], True),
-       ("c'exportDataAsCode", "ExportDataAsCode_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> CString -> IO CBool|], False),
-       ("c'loadFileText", "LoadFileText_", "rl_bindings.h", [t|CString -> IO CString|], True),
-       ("c'unloadFileText", "UnloadFileText_", "rl_bindings.h", [t|CString -> IO ()|], False),
-       ("c'saveFileText", "SaveFileText_", "rl_bindings.h", [t|CString -> CString -> IO CBool|], True),
-       ("c'fileExists", "FileExists_", "rl_bindings.h", [t|CString -> IO CBool|], False),
-       ("c'directoryExists", "DirectoryExists_", "rl_bindings.h", [t|CString -> IO CBool|], False),
-       ("c'isFileExtension", "IsFileExtension_", "rl_bindings.h", [t|CString -> CString -> IO CBool|], False),
-       ("c'getFileLength", "GetFileLength_", "rl_bindings.h", [t|CString -> IO CBool|], False),
-       ("c'getFileExtension", "GetFileExtension_", "rl_bindings.h", [t|CString -> IO CString|], False),
-       ("c'getFileName", "GetFileName_", "rl_bindings.h", [t|CString -> IO CString|], False),
-       ("c'getFileNameWithoutExt", "GetFileNameWithoutExt_", "rl_bindings.h", [t|CString -> IO CString|], False),
-       ("c'getDirectoryPath", "GetDirectoryPath_", "rl_bindings.h", [t|CString -> IO CString|], False),
-       ("c'getPrevDirectoryPath", "GetPrevDirectoryPath_", "rl_bindings.h", [t|CString -> IO CString|], False),
-       ("c'getWorkingDirectory", "GetWorkingDirectory_", "rl_bindings.h", [t|IO CString|], False),
-       ("c'getApplicationDirectory", "GetApplicationDirectory_", "rl_bindings.h", [t|IO CString|], False),
-       ("c'changeDirectory", "ChangeDirectory_", "rl_bindings.h", [t|CString -> IO CBool|], False),
-       ("c'isPathFile", "IsPathFile_", "rl_bindings.h", [t|CString -> IO CBool|], False),
-       ("c'isFileNameValid", "IsFileNameValid_", "rl_bindings.h", [t|CString -> IO CBool|], False),
-       ("c'loadDirectoryFiles", "LoadDirectoryFiles_", "rl_bindings.h", [t|CString -> IO (Ptr FilePathList)|], False),
-       ("c'loadDirectoryFilesEx", "LoadDirectoryFilesEx_", "rl_bindings.h", [t|CString -> CString -> CInt -> IO (Ptr FilePathList)|], False),
-       ("c'unloadDirectoryFiles", "UnloadDirectoryFiles_", "rl_bindings.h", [t|Ptr FilePathList -> IO ()|], False),
-       ("c'isFileDropped", "IsFileDropped_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'loadDroppedFiles", "LoadDroppedFiles_", "rl_bindings.h", [t|IO (Ptr FilePathList)|], False),
-       ("c'unloadDroppedFiles", "UnloadDroppedFiles_", "rl_bindings.h", [t|Ptr FilePathList -> IO ()|], False),
-       ("c'getFileModTime", "GetFileModTime_", "rl_bindings.h", [t|CString -> IO CLong|], False),
-       ("c'compressData", "CompressData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> Ptr CInt -> IO (Ptr CUChar)|], False),
-       ("c'decompressData", "DecompressData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> Ptr CInt -> IO (Ptr CUChar)|], False),
-       ("c'encodeDataBase64", "EncodeDataBase64_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> Ptr CInt -> IO CString|], False),
-       ("c'decodeDataBase64", "DecodeDataBase64_", "rl_bindings.h", [t|Ptr CUChar -> Ptr CInt -> IO (Ptr CUChar)|], False),
-       ("c'loadAutomationEventList", "LoadAutomationEventList_", "rl_bindings.h", [t|CString -> IO (Ptr AutomationEventList)|], False),
-       ("c'exportAutomationEventList", "ExportAutomationEventList_", "rl_bindings.h", [t|Ptr AutomationEventList -> CString -> IO CBool|], False),
-       ("c'setAutomationEventList", "SetAutomationEventList_", "rl_bindings.h", [t|Ptr AutomationEventList -> IO ()|], False),
-       ("c'setAutomationEventBaseFrame", "SetAutomationEventBaseFrame_", "rl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'startAutomationEventRecording", "StartAutomationEventRecording_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'stopAutomationEventRecording", "StopAutomationEventRecording_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'playAutomationEvent", "PlayAutomationEvent", "rl_bindings.h", [t|Ptr AutomationEvent -> IO ()|], False),
-       ("c'isKeyPressed", "IsKeyPressed_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'isKeyPressedRepeat", "IsKeyPressedRepeat_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'isKeyDown", "IsKeyDown_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'isKeyReleased", "IsKeyReleased_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'isKeyUp", "IsKeyUp_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'setExitKey", "SetExitKey_", "rl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'getKeyPressed", "GetKeyPressed_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getCharPressed", "GetCharPressed_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'isGamepadAvailable", "IsGamepadAvailable_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'getGamepadName", "GetGamepadName_", "rl_bindings.h", [t|CInt -> IO CString|], False),
-       ("c'isGamepadButtonPressed", "IsGamepadButtonPressed_", "rl_bindings.h", [t|CInt -> CInt -> IO CBool|], False),
-       ("c'isGamepadButtonDown", "IsGamepadButtonDown_", "rl_bindings.h", [t|CInt -> CInt -> IO CBool|], False),
-       ("c'isGamepadButtonReleased", "IsGamepadButtonReleased_", "rl_bindings.h", [t|CInt -> CInt -> IO CBool|], False),
-       ("c'isGamepadButtonUp", "IsGamepadButtonUp_", "rl_bindings.h", [t|CInt -> CInt -> IO CBool|], False),
-       ("c'getGamepadButtonPressed", "GetGamepadButtonPressed_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getGamepadAxisCount", "GetGamepadAxisCount_", "rl_bindings.h", [t|CInt -> IO CInt|], False),
-       ("c'getGamepadAxisMovement", "GetGamepadAxisMovement_", "rl_bindings.h", [t|CInt -> CInt -> IO CFloat|], False),
-       ("c'setGamepadMappings", "SetGamepadMappings_", "rl_bindings.h", [t|CString -> IO CInt|], False),
-       ("c'setGamepadVibration", "SetGamepadVibration_", "rl_bindings.h", [t|CInt -> CFloat -> CFloat -> IO ()|], False),
-       ("c'isMouseButtonPressed", "IsMouseButtonPressed_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'isMouseButtonDown", "IsMouseButtonDown_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'isMouseButtonReleased", "IsMouseButtonReleased_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'isMouseButtonUp", "IsMouseButtonUp_", "rl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'getMouseX", "GetMouseX_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getMouseY", "GetMouseY_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getMousePosition", "GetMousePosition_", "rl_bindings.h", [t|IO (Ptr Vector2)|], False),
-       ("c'getMouseDelta", "GetMouseDelta_", "rl_bindings.h", [t|IO (Ptr Vector2)|], False),
-       ("c'setMousePosition", "SetMousePosition_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'setMouseOffset", "SetMouseOffset_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'setMouseScale", "SetMouseScale_", "rl_bindings.h", [t|CFloat -> CFloat -> IO ()|], False),
-       ("c'getMouseWheelMove", "GetMouseWheelMove_", "rl_bindings.h", [t|IO CFloat|], False),
-       ("c'getMouseWheelMoveV", "GetMouseWheelMoveV_", "rl_bindings.h", [t|IO (Ptr Vector2)|], False),
-       ("c'setMouseCursor", "SetMouseCursor_", "rl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'getTouchX", "GetTouchX_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getTouchY", "GetTouchY_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getTouchPosition", "GetTouchPosition_", "rl_bindings.h", [t|CInt -> IO (Ptr Vector2)|], False),
-       ("c'getTouchPointId", "GetTouchPointId_", "rl_bindings.h", [t|CInt -> IO CInt|], False),
-       ("c'getTouchPointCount", "GetTouchPointCount_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'setGesturesEnabled", "SetGesturesEnabled_", "rl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'isGestureDetected", "IsGestureDetected_", "rl_bindings.h", [t|CUInt -> IO CBool|], False),
-       ("c'getGestureDetected", "GetGestureDetected_", "rl_bindings.h", [t|IO CInt|], False),
-       ("c'getGestureHoldDuration", "GetGestureHoldDuration_", "rl_bindings.h", [t|IO CFloat|], False),
-       ("c'getGestureDragVector", "GetGestureDragVector_", "rl_bindings.h", [t|IO (Ptr Vector2)|], False),
-       ("c'getGestureDragAngle", "GetGestureDragAngle_", "rl_bindings.h", [t|IO CFloat|], False),
-       ("c'getGesturePinchVector", "GetGesturePinchVector_", "rl_bindings.h", [t|IO (Ptr Vector2)|], False),
-       ("c'getGesturePinchAngle", "GetGesturePinchAngle_", "rl_bindings.h", [t|IO CFloat|], False)
+     [ ("c'initWindow", "InitWindow_", "rl_bindings.h", [t|CInt -> CInt -> CString -> IO ()|]),
+       ("c'windowShouldClose", "WindowShouldClose_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'closeWindow", "CloseWindow_", "rl_bindings.h", [t|IO ()|]),
+       ("c'isWindowReady", "IsWindowReady_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'isWindowFullscreen", "IsWindowFullscreen_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'isWindowHidden", "IsWindowHidden_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'isWindowMinimized", "IsWindowMinimized_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'isWindowMaximized", "IsWindowMaximized_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'isWindowFocused", "IsWindowFocused_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'isWindowResized", "IsWindowResized_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'isWindowState", "IsWindowState_", "rl_bindings.h", [t|CUInt -> IO CBool|]),
+       ("c'setWindowState", "SetWindowState_", "rl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'clearWindowState", "ClearWindowState_", "rl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'toggleFullscreen", "ToggleFullscreen_", "rl_bindings.h", [t|IO ()|]),
+       ("c'toggleBorderlessWindowed", "ToggleBorderlessWindowed_", "rl_bindings.h", [t|IO ()|]),
+       ("c'maximizeWindow", "MaximizeWindow_", "rl_bindings.h", [t|IO ()|]),
+       ("c'minimizeWindow", "MinimizeWindow_", "rl_bindings.h", [t|IO ()|]),
+       ("c'restoreWindow", "RestoreWindow_", "rl_bindings.h", [t|IO ()|]),
+       ("c'setWindowIcon", "SetWindowIcon_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'setWindowIcons", "SetWindowIcons_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|]),
+       ("c'setWindowTitle", "SetWindowTitle_", "rl_bindings.h", [t|CString -> IO ()|]),
+       ("c'setWindowPosition", "SetWindowPosition_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'setWindowMonitor", "SetWindowMonitor_", "rl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'setWindowMinSize", "SetWindowMinSize_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'setWindowMaxSize", "SetWindowMaxSize_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'setWindowSize", "SetWindowSize_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'setWindowOpacity", "SetWindowOpacity_", "rl_bindings.h", [t|CFloat -> IO ()|]),
+       ("c'setWindowFocused", "SetWindowFocused_", "rl_bindings.h", [t|IO ()|]),
+       ("c'getWindowHandle", "GetWindowHandle_", "rl_bindings.h", [t|IO (Ptr ())|]),
+       ("c'getScreenWidth", "GetScreenWidth_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getScreenHeight", "GetScreenHeight_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getRenderWidth", "GetRenderWidth_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getRenderHeight", "GetRenderHeight_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getMonitorCount", "GetMonitorCount_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getCurrentMonitor", "GetCurrentMonitor_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getMonitorPosition", "GetMonitorPosition_", "rl_bindings.h", [t|CInt -> IO (Ptr Vector2)|]),
+       ("c'getMonitorWidth", "GetMonitorWidth_", "rl_bindings.h", [t|CInt -> IO CInt|]),
+       ("c'getMonitorHeight", "GetMonitorHeight_", "rl_bindings.h", [t|CInt -> IO CInt|]),
+       ("c'getMonitorPhysicalWidth", "GetMonitorPhysicalWidth_", "rl_bindings.h", [t|CInt -> IO CInt|]),
+       ("c'getMonitorPhysicalHeight", "GetMonitorPhysicalHeight_", "rl_bindings.h", [t|CInt -> IO CInt|]),
+       ("c'getMonitorRefreshRate", "GetMonitorRefreshRate_", "rl_bindings.h", [t|CInt -> IO CInt|]),
+       ("c'getWindowPosition", "GetWindowPosition_", "rl_bindings.h", [t|IO (Ptr Vector2)|]),
+       ("c'getWindowScaleDPI", "GetWindowScaleDPI_", "rl_bindings.h", [t|IO (Ptr Vector2)|]),
+       ("c'getMonitorName", "GetMonitorName_", "rl_bindings.h", [t|CInt -> IO CString|]),
+       ("c'setClipboardText", "SetClipboardText_", "rl_bindings.h", [t|CString -> IO ()|]),
+       ("c'getClipboardText", "GetClipboardText_", "rl_bindings.h", [t|IO CString|]),
+       ("c'enableEventWaiting", "EnableEventWaiting_", "rl_bindings.h", [t|IO ()|]),
+       ("c'disableEventWaiting", "DisableEventWaiting_", "rl_bindings.h", [t|IO ()|]),
+       ("c'swapScreenBuffer", "SwapScreenBuffer_", "rl_bindings.h", [t|IO ()|]),
+       ("c'pollInputEvents", "PollInputEvents_", "rl_bindings.h", [t|IO ()|]),
+       ("c'waitTime", "WaitTime_", "rl_bindings.h", [t|CDouble -> IO ()|]),
+       ("c'showCursor", "ShowCursor_", "rl_bindings.h", [t|IO ()|]),
+       ("c'hideCursor", "HideCursor_", "rl_bindings.h", [t|IO ()|]),
+       ("c'isCursorHidden", "IsCursorHidden_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'enableCursor", "EnableCursor_", "rl_bindings.h", [t|IO ()|]),
+       ("c'disableCursor", "DisableCursor_", "rl_bindings.h", [t|IO ()|]),
+       ("c'isCursorOnScreen", "IsCursorOnScreen_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'clearBackground", "ClearBackground_", "rl_bindings.h", [t|Ptr Color -> IO ()|]),
+       ("c'beginDrawing", "BeginDrawing_", "rl_bindings.h", [t|IO ()|]),
+       ("c'endDrawing", "EndDrawing_", "rl_bindings.h", [t|IO ()|]),
+       ("c'beginMode2D", "BeginMode2D_", "rl_bindings.h", [t|Ptr Camera2D -> IO ()|]),
+       ("c'endMode2D", "EndMode2D_", "rl_bindings.h", [t|IO ()|]),
+       ("c'beginMode3D", "BeginMode3D_", "rl_bindings.h", [t|Ptr Camera3D -> IO ()|]),
+       ("c'endMode3D", "EndMode3D_", "rl_bindings.h", [t|IO ()|]),
+       ("c'beginTextureMode", "BeginTextureMode_", "rl_bindings.h", [t|Ptr RenderTexture -> IO ()|]),
+       ("c'endTextureMode", "EndTextureMode_", "rl_bindings.h", [t|IO ()|]),
+       ("c'beginShaderMode", "BeginShaderMode_", "rl_bindings.h", [t|Ptr Shader -> IO ()|]),
+       ("c'endShaderMode", "EndShaderMode_", "rl_bindings.h", [t|IO ()|]),
+       ("c'beginBlendMode", "BeginBlendMode_", "rl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'endBlendMode", "EndBlendMode_", "rl_bindings.h", [t|IO ()|]),
+       ("c'beginScissorMode", "BeginScissorMode_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> IO ()|]),
+       ("c'endScissorMode", "EndScissorMode_", "rl_bindings.h", [t|IO ()|]),
+       ("c'beginVrStereoMode", "BeginVrStereoMode_", "rl_bindings.h", [t|Ptr VrStereoConfig -> IO ()|]),
+       ("c'endVrStereoMode", "EndVrStereoMode_", "rl_bindings.h", [t|IO ()|]),
+       ("c'loadVrStereoConfig", "LoadVrStereoConfig_", "rl_bindings.h", [t|Ptr VrDeviceInfo -> IO (Ptr VrStereoConfig)|]),
+       ("c'unloadVrStereoConfig", "UnloadVrStereoConfig_", "rl_bindings.h", [t|Ptr VrStereoConfig -> IO ()|]),
+       ("c'loadShader", "LoadShader_", "rl_bindings.h", [t|CString -> CString -> IO (Ptr Shader)|]),
+       ("c'loadShaderFromMemory", "LoadShaderFromMemory_", "rl_bindings.h", [t|CString -> CString -> IO (Ptr Shader)|]),
+       ("c'isShaderReady", "IsShaderReady_", "rl_bindings.h", [t|Ptr Shader -> IO CBool|]),
+       ("c'getShaderLocation", "GetShaderLocation_", "rl_bindings.h", [t|Ptr Shader -> CString -> IO CInt|]),
+       ("c'getShaderLocationAttrib", "GetShaderLocationAttrib_", "rl_bindings.h", [t|Ptr Shader -> CString -> IO CInt|]),
+       ("c'setShaderValue", "SetShaderValue_", "rl_bindings.h", [t|Ptr Shader -> CInt -> Ptr () -> CInt -> IO ()|]),
+       ("c'setShaderValueV", "SetShaderValueV_", "rl_bindings.h", [t|Ptr Shader -> CInt -> Ptr () -> CInt -> CInt -> IO ()|]),
+       ("c'setShaderValueMatrix", "SetShaderValueMatrix_", "rl_bindings.h", [t|Ptr Shader -> CInt -> Ptr Matrix -> IO ()|]),
+       ("c'setShaderValueTexture", "SetShaderValueTexture_", "rl_bindings.h", [t|Ptr Shader -> CInt -> Ptr Texture -> IO ()|]),
+       ("c'unloadShader", "UnloadShader_", "rl_bindings.h", [t|Ptr Shader -> IO ()|]),
+       ("c'getScreenToWorldRay", "GetScreenToWorldRay_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Camera3D -> IO (Ptr Ray)|]),
+       ("c'getScreenToWorldRayEx", "GetScreenToWorldRayEx_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Camera3D -> CFloat -> CFloat -> IO (Ptr Ray)|]),
+       ("c'getCameraMatrix", "GetCameraMatrix_", "rl_bindings.h", [t|Ptr Camera3D -> IO (Ptr Matrix)|]),
+       ("c'getCameraMatrix2D", "GetCameraMatrix2D_", "rl_bindings.h", [t|Ptr Camera2D -> IO (Ptr Matrix)|]),
+       ("c'getWorldToScreen", "GetWorldToScreen_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Camera3D -> IO (Ptr Vector2)|]),
+       ("c'getScreenToWorld2D", "GetScreenToWorld2D_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Camera2D -> IO (Ptr Vector2)|]),
+       ("c'getWorldToScreenEx", "GetWorldToScreenEx_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Camera3D -> CInt -> CInt -> IO (Ptr Vector2)|]),
+       ("c'getWorldToScreen2D", "GetWorldToScreen2D_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Camera2D -> IO (Ptr Vector2)|]),
+       ("c'setTargetFPS", "SetTargetFPS_", "rl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'getFPS", "GetFPS_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getFrameTime", "GetFrameTime_", "rl_bindings.h", [t|IO CFloat|]),
+       ("c'getTime", "GetTime_", "rl_bindings.h", [t|IO CDouble|]),
+       ("c'setRandomSeed", "SetRandomSeed_", "rl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'getRandomValue", "GetRandomValue_", "rl_bindings.h", [t|CInt -> CInt -> IO CInt|]),
+       ("c'loadRandomSequence", "LoadRandomSequence_", "rl_bindings.h", [t|CUInt -> CInt -> CInt -> IO (Ptr CInt)|]),
+       ("c'takeScreenshot", "TakeScreenshot_", "rl_bindings.h", [t|CString -> IO ()|]),
+       ("c'setConfigFlags", "SetConfigFlags_", "rl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'traceLog", "TraceLog_", "rl_bindings.h", [t|CInt -> CString -> IO ()|]), -- Uses varags, can't implement complete functionality
+       ("c'setTraceLogLevel", "SetTraceLogLevel_", "rl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'memAlloc", "MemAlloc_", "rl_bindings.h", [t|CInt -> IO (Ptr ())|]),
+       ("c'memRealloc", "MemRealloc_", "rl_bindings.h", [t|Ptr () -> CInt -> IO (Ptr ())|]),
+       ("c'memFree", "MemFree_", "rl_bindings.h", [t|Ptr () -> IO ()|]),
+       ("c'openURL", "OpenURL_", "rl_bindings.h", [t|CString -> IO ()|]),
+       ("c'setTraceLogCallback", "SetTraceLogCallback_", "rl_bindings.h", [t|C'TraceLogCallback -> IO ()|]),
+       ("c'setLoadFileDataCallback", "SetLoadFileDataCallback_", "rl_bindings.h", [t|C'LoadFileDataCallback -> IO ()|]),
+       ("c'setSaveFileDataCallback", "SetSaveFileDataCallback_", "rl_bindings.h", [t|C'SaveFileDataCallback -> IO ()|]),
+       ("c'setLoadFileTextCallback", "SetLoadFileTextCallback_", "rl_bindings.h", [t|C'LoadFileTextCallback -> IO ()|]),
+       ("c'setSaveFileTextCallback", "SetSaveFileTextCallback_", "rl_bindings.h", [t|C'SaveFileTextCallback -> IO ()|]),
+       ("c'loadFileData", "LoadFileData_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr CUChar)|]),
+       ("c'unloadFileData", "UnloadFileData_", "rl_bindings.h", [t|Ptr CUChar -> IO ()|]),
+       ("c'saveFileData", "SaveFileData_", "rl_bindings.h", [t|CString -> Ptr () -> CInt -> IO CBool|]),
+       ("c'exportDataAsCode", "ExportDataAsCode_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> CString -> IO CBool|]),
+       ("c'loadFileText", "LoadFileText_", "rl_bindings.h", [t|CString -> IO CString|]),
+       ("c'unloadFileText", "UnloadFileText_", "rl_bindings.h", [t|CString -> IO ()|]),
+       ("c'saveFileText", "SaveFileText_", "rl_bindings.h", [t|CString -> CString -> IO CBool|]),
+       ("c'fileExists", "FileExists_", "rl_bindings.h", [t|CString -> IO CBool|]),
+       ("c'directoryExists", "DirectoryExists_", "rl_bindings.h", [t|CString -> IO CBool|]),
+       ("c'isFileExtension", "IsFileExtension_", "rl_bindings.h", [t|CString -> CString -> IO CBool|]),
+       ("c'getFileLength", "GetFileLength_", "rl_bindings.h", [t|CString -> IO CBool|]),
+       ("c'getFileExtension", "GetFileExtension_", "rl_bindings.h", [t|CString -> IO CString|]),
+       ("c'getFileName", "GetFileName_", "rl_bindings.h", [t|CString -> IO CString|]),
+       ("c'getFileNameWithoutExt", "GetFileNameWithoutExt_", "rl_bindings.h", [t|CString -> IO CString|]),
+       ("c'getDirectoryPath", "GetDirectoryPath_", "rl_bindings.h", [t|CString -> IO CString|]),
+       ("c'getPrevDirectoryPath", "GetPrevDirectoryPath_", "rl_bindings.h", [t|CString -> IO CString|]),
+       ("c'getWorkingDirectory", "GetWorkingDirectory_", "rl_bindings.h", [t|IO CString|]),
+       ("c'getApplicationDirectory", "GetApplicationDirectory_", "rl_bindings.h", [t|IO CString|]),
+       ("c'makeDirectory", "MakeDirectory_", "rl_bindings.h", [t|CString -> IO CInt|]),
+       ("c'changeDirectory", "ChangeDirectory_", "rl_bindings.h", [t|CString -> IO CBool|]),
+       ("c'isPathFile", "IsPathFile_", "rl_bindings.h", [t|CString -> IO CBool|]),
+       ("c'isFileNameValid", "IsFileNameValid_", "rl_bindings.h", [t|CString -> IO CBool|]),
+       ("c'loadDirectoryFiles", "LoadDirectoryFiles_", "rl_bindings.h", [t|CString -> IO (Ptr FilePathList)|]),
+       ("c'loadDirectoryFilesEx", "LoadDirectoryFilesEx_", "rl_bindings.h", [t|CString -> CString -> CInt -> IO (Ptr FilePathList)|]),
+       ("c'unloadDirectoryFiles", "UnloadDirectoryFiles_", "rl_bindings.h", [t|Ptr FilePathList -> IO ()|]),
+       ("c'isFileDropped", "IsFileDropped_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'loadDroppedFiles", "LoadDroppedFiles_", "rl_bindings.h", [t|IO (Ptr FilePathList)|]),
+       ("c'unloadDroppedFiles", "UnloadDroppedFiles_", "rl_bindings.h", [t|Ptr FilePathList -> IO ()|]),
+       ("c'getFileModTime", "GetFileModTime_", "rl_bindings.h", [t|CString -> IO CLong|]),
+       ("c'compressData", "CompressData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> Ptr CInt -> IO (Ptr CUChar)|]),
+       ("c'decompressData", "DecompressData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> Ptr CInt -> IO (Ptr CUChar)|]),
+       ("c'encodeDataBase64", "EncodeDataBase64_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> Ptr CInt -> IO CString|]),
+       ("c'decodeDataBase64", "DecodeDataBase64_", "rl_bindings.h", [t|Ptr CUChar -> Ptr CInt -> IO (Ptr CUChar)|]),
+       ("c'loadAutomationEventList", "LoadAutomationEventList_", "rl_bindings.h", [t|CString -> IO (Ptr AutomationEventList)|]),
+       ("c'exportAutomationEventList", "ExportAutomationEventList_", "rl_bindings.h", [t|Ptr AutomationEventList -> CString -> IO CBool|]),
+       ("c'setAutomationEventList", "SetAutomationEventList_", "rl_bindings.h", [t|Ptr AutomationEventList -> IO ()|]),
+       ("c'setAutomationEventBaseFrame", "SetAutomationEventBaseFrame_", "rl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'startAutomationEventRecording", "StartAutomationEventRecording_", "rl_bindings.h", [t|IO ()|]),
+       ("c'stopAutomationEventRecording", "StopAutomationEventRecording_", "rl_bindings.h", [t|IO ()|]),
+       ("c'playAutomationEvent", "PlayAutomationEvent", "rl_bindings.h", [t|Ptr AutomationEvent -> IO ()|]),
+       ("c'isKeyPressed", "IsKeyPressed_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'isKeyPressedRepeat", "IsKeyPressedRepeat_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'isKeyDown", "IsKeyDown_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'isKeyReleased", "IsKeyReleased_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'isKeyUp", "IsKeyUp_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'setExitKey", "SetExitKey_", "rl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'getKeyPressed", "GetKeyPressed_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getCharPressed", "GetCharPressed_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'isGamepadAvailable", "IsGamepadAvailable_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'getGamepadName", "GetGamepadName_", "rl_bindings.h", [t|CInt -> IO CString|]),
+       ("c'isGamepadButtonPressed", "IsGamepadButtonPressed_", "rl_bindings.h", [t|CInt -> CInt -> IO CBool|]),
+       ("c'isGamepadButtonDown", "IsGamepadButtonDown_", "rl_bindings.h", [t|CInt -> CInt -> IO CBool|]),
+       ("c'isGamepadButtonReleased", "IsGamepadButtonReleased_", "rl_bindings.h", [t|CInt -> CInt -> IO CBool|]),
+       ("c'isGamepadButtonUp", "IsGamepadButtonUp_", "rl_bindings.h", [t|CInt -> CInt -> IO CBool|]),
+       ("c'getGamepadButtonPressed", "GetGamepadButtonPressed_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getGamepadAxisCount", "GetGamepadAxisCount_", "rl_bindings.h", [t|CInt -> IO CInt|]),
+       ("c'getGamepadAxisMovement", "GetGamepadAxisMovement_", "rl_bindings.h", [t|CInt -> CInt -> IO CFloat|]),
+       ("c'setGamepadMappings", "SetGamepadMappings_", "rl_bindings.h", [t|CString -> IO CInt|]),
+       ("c'setGamepadVibration", "SetGamepadVibration_", "rl_bindings.h", [t|CInt -> CFloat -> CFloat -> IO ()|]),
+       ("c'isMouseButtonPressed", "IsMouseButtonPressed_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'isMouseButtonDown", "IsMouseButtonDown_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'isMouseButtonReleased", "IsMouseButtonReleased_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'isMouseButtonUp", "IsMouseButtonUp_", "rl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'getMouseX", "GetMouseX_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getMouseY", "GetMouseY_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getMousePosition", "GetMousePosition_", "rl_bindings.h", [t|IO (Ptr Vector2)|]),
+       ("c'getMouseDelta", "GetMouseDelta_", "rl_bindings.h", [t|IO (Ptr Vector2)|]),
+       ("c'setMousePosition", "SetMousePosition_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'setMouseOffset", "SetMouseOffset_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'setMouseScale", "SetMouseScale_", "rl_bindings.h", [t|CFloat -> CFloat -> IO ()|]),
+       ("c'getMouseWheelMove", "GetMouseWheelMove_", "rl_bindings.h", [t|IO CFloat|]),
+       ("c'getMouseWheelMoveV", "GetMouseWheelMoveV_", "rl_bindings.h", [t|IO (Ptr Vector2)|]),
+       ("c'setMouseCursor", "SetMouseCursor_", "rl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'getTouchX", "GetTouchX_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getTouchY", "GetTouchY_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getTouchPosition", "GetTouchPosition_", "rl_bindings.h", [t|CInt -> IO (Ptr Vector2)|]),
+       ("c'getTouchPointId", "GetTouchPointId_", "rl_bindings.h", [t|CInt -> IO CInt|]),
+       ("c'getTouchPointCount", "GetTouchPointCount_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'setGesturesEnabled", "SetGesturesEnabled_", "rl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'isGestureDetected", "IsGestureDetected_", "rl_bindings.h", [t|CUInt -> IO CBool|]),
+       ("c'getGestureDetected", "GetGestureDetected_", "rl_bindings.h", [t|IO CInt|]),
+       ("c'getGestureHoldDuration", "GetGestureHoldDuration_", "rl_bindings.h", [t|IO CFloat|]),
+       ("c'getGestureDragVector", "GetGestureDragVector_", "rl_bindings.h", [t|IO (Ptr Vector2)|]),
+       ("c'getGestureDragAngle", "GetGestureDragAngle_", "rl_bindings.h", [t|IO CFloat|]),
+       ("c'getGesturePinchVector", "GetGesturePinchVector_", "rl_bindings.h", [t|IO (Ptr Vector2)|]),
+       ("c'getGesturePinchAngle", "GetGesturePinchAngle_", "rl_bindings.h", [t|IO CFloat|])
      ]
  )
 
@@ -1053,29 +1058,30 @@
 openURL :: String -> IO ()
 openURL url = withCString url c'openURL
 
-setLoadFileDataCallback :: LoadFileDataCallback -> IO C'LoadFileDataCallback
+setTraceLogCallback :: TraceLogCallback -> IO ()
+setTraceLogCallback callback = do
+  c <- createTraceLogCallback callback
+  c'setTraceLogCallback c
+
+setLoadFileDataCallback :: LoadFileDataCallback -> IO ()
 setLoadFileDataCallback callback = do
   c <- createLoadFileDataCallback callback
   c'setLoadFileDataCallback c
-  return c
 
-setSaveFileDataCallback :: (Storable a) => SaveFileDataCallback a -> IO C'SaveFileDataCallback
+setSaveFileDataCallback :: (Storable a) => SaveFileDataCallback a -> IO ()
 setSaveFileDataCallback callback = do
   c <- createSaveFileDataCallback callback
   c'setSaveFileDataCallback c
-  return c
 
-setLoadFileTextCallback :: LoadFileTextCallback -> IO C'LoadFileTextCallback
+setLoadFileTextCallback :: LoadFileTextCallback -> IO ()
 setLoadFileTextCallback callback = do
   c <- createLoadFileTextCallback callback
   c'setLoadFileTextCallback c
-  return c
 
-setSaveFileTextCallback :: SaveFileTextCallback -> IO C'SaveFileTextCallback
+setSaveFileTextCallback :: SaveFileTextCallback -> IO ()
 setSaveFileTextCallback callback = do
   c <- createSaveFileTextCallback callback
   c'setSaveFileTextCallback c
-  return c
 
 loadFileData :: String -> IO [Integer]
 loadFileData fileName =
@@ -1138,6 +1144,9 @@
 getApplicationDirectory :: IO String
 getApplicationDirectory = c'getApplicationDirectory >>= peekCString
 
+makeDirectory :: String -> IO Bool
+makeDirectory dirPath = (== 0) <$> withCString dirPath c'makeDirectory
+
 changeDirectory :: String -> IO Bool
 changeDirectory dir = toBool <$> withCString dir c'changeDirectory
 
@@ -1401,36 +1410,33 @@
 getGesturePinchAngle = realToFrac <$> c'getGesturePinchAngle
 
 foreign import ccall unsafe "wrapper"
+  mk'traceLogCallback ::
+    (CInt -> CString -> IO ()) -> IO C'TraceLogCallback
+
+foreign import ccall unsafe "wrapper"
   mk'loadFileDataCallback ::
     (CString -> Ptr CUInt -> IO (Ptr CUChar)) -> IO C'LoadFileDataCallback
 
--- foreign import ccall unsafe "dynamic"
---   mK'loadFileDataCallback ::
---     C'LoadFileDataCallback -> (CString -> Ptr CUInt -> IO (Ptr CUChar))
-
 foreign import ccall unsafe "wrapper"
   mk'saveFileDataCallback ::
     (CString -> Ptr () -> CUInt -> IO CInt) -> IO C'SaveFileDataCallback
 
--- foreign import ccall unsafe "dynamic"
---   mK'saveFileDataCallback ::
---     C'SaveFileDataCallback -> (CString -> Ptr () -> CUInt -> IO CInt)
-
 foreign import ccall unsafe "wrapper"
   mk'loadFileTextCallback ::
     (CString -> IO CString) -> IO C'LoadFileTextCallback
 
--- foreign import ccall unsafe "dynamic"
---   mK'loadFileTextCallback ::
---     C'LoadFileTextCallback -> (CString -> IO CString)
-
 foreign import ccall unsafe "wrapper"
   mk'saveFileTextCallback ::
     (CString -> CString -> IO CInt) -> IO C'SaveFileTextCallback
 
--- foreign import ccall unsafe "dynamic"
---   mK'saveFileTextCallback ::
---     C'SaveFileTextCallback -> (CString -> CString -> IO CInt)
+createTraceLogCallback :: TraceLogCallback -> IO C'TraceLogCallback
+createTraceLogCallback callback =
+  mk'traceLogCallback
+    (\logLevel text ->
+        do
+          t <- peekCString text
+          callback (toEnum (fromIntegral logLevel)) t
+    )
 
 createLoadFileDataCallback :: LoadFileDataCallback -> IO C'LoadFileDataCallback
 createLoadFileDataCallback callback =
diff --git a/src/Raylib/Core/Audio.hs b/src/Raylib/Core/Audio.hs
--- a/src/Raylib/Core/Audio.hs
+++ b/src/Raylib/Core/Audio.hs
@@ -171,72 +171,72 @@
   )
 
 $( genNative
-     [ ("c'initAudioDevice", "InitAudioDevice_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'closeAudioDevice", "CloseAudioDevice_", "rl_bindings.h", [t|IO ()|], False),
-       ("c'isAudioDeviceReady", "IsAudioDeviceReady_", "rl_bindings.h", [t|IO CBool|], False),
-       ("c'setMasterVolume", "SetMasterVolume_", "rl_bindings.h", [t|CFloat -> IO ()|], False),
-       ("c'getMasterVolume", "GetMasterVolume_", "rl_bindings.h", [t|IO CFloat|], False),
-       ("c'loadWave", "LoadWave_", "rl_bindings.h", [t|CString -> IO (Ptr Wave)|], False),
-       ("c'loadWaveFromMemory", "LoadWaveFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> IO (Ptr Wave)|], False),
-       ("c'loadSound", "LoadSound_", "rl_bindings.h", [t|CString -> IO (Ptr Sound)|], False),
-       ("c'loadSoundFromWave", "LoadSoundFromWave_", "rl_bindings.h", [t|Ptr Wave -> IO (Ptr Sound)|], False),
-       ("c'loadSoundAlias", "LoadSoundAlias_", "rl_bindings.h", [t|Ptr Sound -> IO (Ptr Sound)|], False),
-       ("c'updateSound", "UpdateSound_", "rl_bindings.h", [t|Ptr Sound -> Ptr () -> CInt -> IO ()|], False),
-       ("c'isWaveReady", "IsWaveReady_", "rl_bindings.h", [t|Ptr Wave -> IO CBool|], False),
-       ("c'unloadWave", "UnloadWave_", "rl_bindings.h", [t|Ptr Wave -> IO ()|], False),
-       ("c'isSoundReady", "IsSoundReady_", "rl_bindings.h", [t|Ptr Sound -> IO CBool|], False),
-       ("c'unloadSound", "UnloadSound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|], False),
-       ("c'unloadSoundAlias", "UnloadSoundAlias_", "rl_bindings.h", [t|Ptr Sound -> IO ()|], False),
-       ("c'exportWave", "ExportWave_", "rl_bindings.h", [t|Ptr Wave -> CString -> IO CBool|], False),
-       ("c'exportWaveAsCode", "ExportWaveAsCode_", "rl_bindings.h", [t|Ptr Wave -> CString -> IO CBool|], False),
-       ("c'playSound", "PlaySound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|], False),
-       ("c'stopSound", "StopSound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|], False),
-       ("c'pauseSound", "PauseSound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|], False),
-       ("c'resumeSound", "ResumeSound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|], False),
-       ("c'isSoundPlaying", "IsSoundPlaying_", "rl_bindings.h", [t|Ptr Sound -> IO CBool|], False),
-       ("c'setSoundVolume", "SetSoundVolume_", "rl_bindings.h", [t|Ptr Sound -> CFloat -> IO ()|], False),
-       ("c'setSoundPitch", "SetSoundPitch_", "rl_bindings.h", [t|Ptr Sound -> CFloat -> IO ()|], False),
-       ("c'setSoundPan", "SetSoundPan_", "rl_bindings.h", [t|Ptr Sound -> CFloat -> IO ()|], False),
-       ("c'waveCopy", "WaveCopy_", "rl_bindings.h", [t|Ptr Wave -> IO (Ptr Wave)|], False),
-       ("c'waveCrop", "WaveCrop_", "rl_bindings.h", [t|Ptr Wave -> CInt -> CInt -> IO ()|], False),
-       ("c'waveFormat", "WaveFormat_", "rl_bindings.h", [t|Ptr Wave -> CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'loadWaveSamples", "LoadWaveSamples_", "rl_bindings.h", [t|Ptr Wave -> IO (Ptr CFloat)|], False),
-       ("c'unloadWaveSamples", "UnloadWaveSamples_", "rl_bindings.h", [t|Ptr CFloat -> IO ()|], False),
-       ("c'loadMusicStream", "LoadMusicStream_", "rl_bindings.h", [t|CString -> IO (Ptr Music)|], False),
-       ("c'loadMusicStreamFromMemory", "LoadMusicStreamFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> IO (Ptr Music)|], False),
-       ("c'isMusicReady", "IsMusicReady_", "rl_bindings.h", [t|Ptr Music -> IO CBool|], False),
-       ("c'unloadMusicStream", "UnloadMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|], False),
-       ("c'playMusicStream", "PlayMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|], False),
-       ("c'isMusicStreamPlaying", "IsMusicStreamPlaying_", "rl_bindings.h", [t|Ptr Music -> IO CBool|], False),
-       ("c'updateMusicStream", "UpdateMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|], False),
-       ("c'stopMusicStream", "StopMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|], False),
-       ("c'pauseMusicStream", "PauseMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|], False),
-       ("c'resumeMusicStream", "ResumeMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|], False),
-       ("c'seekMusicStream", "SeekMusicStream_", "rl_bindings.h", [t|Ptr Music -> CFloat -> IO ()|], False),
-       ("c'setMusicVolume", "SetMusicVolume_", "rl_bindings.h", [t|Ptr Music -> CFloat -> IO ()|], False),
-       ("c'setMusicPitch", "SetMusicPitch_", "rl_bindings.h", [t|Ptr Music -> CFloat -> IO ()|], False),
-       ("c'setMusicPan", "SetMusicPan_", "rl_bindings.h", [t|Ptr Music -> CFloat -> IO ()|], False),
-       ("c'getMusicTimeLength", "GetMusicTimeLength_", "rl_bindings.h", [t|Ptr Music -> IO CFloat|], False),
-       ("c'getMusicTimePlayed", "GetMusicTimePlayed_", "rl_bindings.h", [t|Ptr Music -> IO CFloat|], False),
-       ("c'loadAudioStream", "LoadAudioStream_", "rl_bindings.h", [t|CUInt -> CUInt -> CUInt -> IO (Ptr AudioStream)|], False),
-       ("c'isAudioStreamReady", "IsAudioStreamReady_", "rl_bindings.h", [t|Ptr AudioStream -> IO CBool|], False),
-       ("c'unloadAudioStream", "UnloadAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|], False),
-       ("c'updateAudioStream", "UpdateAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> Ptr () -> CInt -> IO ()|], False),
-       ("c'isAudioStreamProcessed", "IsAudioStreamProcessed_", "rl_bindings.h", [t|Ptr AudioStream -> IO CBool|], False),
-       ("c'playAudioStream", "PlayAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|], False),
-       ("c'pauseAudioStream", "PauseAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|], False),
-       ("c'resumeAudioStream", "ResumeAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|], False),
-       ("c'isAudioStreamPlaying", "IsAudioStreamPlaying_", "rl_bindings.h", [t|Ptr AudioStream -> IO CBool|], False),
-       ("c'stopAudioStream", "StopAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|], False),
-       ("c'setAudioStreamVolume", "SetAudioStreamVolume_", "rl_bindings.h", [t|Ptr AudioStream -> CFloat -> IO ()|], False),
-       ("c'setAudioStreamPitch", "SetAudioStreamPitch_", "rl_bindings.h", [t|Ptr AudioStream -> CFloat -> IO ()|], False),
-       ("c'setAudioStreamPan", "SetAudioStreamPan_", "rl_bindings.h", [t|Ptr AudioStream -> CFloat -> IO ()|], False),
-       ("c'setAudioStreamBufferSizeDefault", "SetAudioStreamBufferSizeDefault_", "rl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'setAudioStreamCallback", "SetAudioStreamCallback_", "rl_bindings.h", [t|Ptr AudioStream -> C'AudioCallback -> IO ()|], False),
-       ("c'attachAudioStreamProcessor", "AttachAudioStreamProcessor_", "rl_bindings.h", [t|Ptr AudioStream -> C'AudioCallback -> IO ()|], False),
-       ("c'detachAudioStreamProcessor", "DetachAudioStreamProcessor_", "rl_bindings.h", [t|Ptr AudioStream -> C'AudioCallback -> IO ()|], False),
-       ("c'attachAudioMixedProcessor", "AttachAudioMixedProcessor_", "rl_bindings.h", [t|C'AudioCallback -> IO ()|], False),
-       ("c'detachAudioMixedProcessor", "DetachAudioMixedProcessor_", "rl_bindings.h", [t|C'AudioCallback -> IO ()|], False)
+     [ ("c'initAudioDevice", "InitAudioDevice_", "rl_bindings.h", [t|IO ()|]),
+       ("c'closeAudioDevice", "CloseAudioDevice_", "rl_bindings.h", [t|IO ()|]),
+       ("c'isAudioDeviceReady", "IsAudioDeviceReady_", "rl_bindings.h", [t|IO CBool|]),
+       ("c'setMasterVolume", "SetMasterVolume_", "rl_bindings.h", [t|CFloat -> IO ()|]),
+       ("c'getMasterVolume", "GetMasterVolume_", "rl_bindings.h", [t|IO CFloat|]),
+       ("c'loadWave", "LoadWave_", "rl_bindings.h", [t|CString -> IO (Ptr Wave)|]),
+       ("c'loadWaveFromMemory", "LoadWaveFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> IO (Ptr Wave)|]),
+       ("c'loadSound", "LoadSound_", "rl_bindings.h", [t|CString -> IO (Ptr Sound)|]),
+       ("c'loadSoundFromWave", "LoadSoundFromWave_", "rl_bindings.h", [t|Ptr Wave -> IO (Ptr Sound)|]),
+       ("c'loadSoundAlias", "LoadSoundAlias_", "rl_bindings.h", [t|Ptr Sound -> IO (Ptr Sound)|]),
+       ("c'updateSound", "UpdateSound_", "rl_bindings.h", [t|Ptr Sound -> Ptr () -> CInt -> IO ()|]),
+       ("c'isWaveReady", "IsWaveReady_", "rl_bindings.h", [t|Ptr Wave -> IO CBool|]),
+       ("c'unloadWave", "UnloadWave_", "rl_bindings.h", [t|Ptr Wave -> IO ()|]),
+       ("c'isSoundReady", "IsSoundReady_", "rl_bindings.h", [t|Ptr Sound -> IO CBool|]),
+       ("c'unloadSound", "UnloadSound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|]),
+       ("c'unloadSoundAlias", "UnloadSoundAlias_", "rl_bindings.h", [t|Ptr Sound -> IO ()|]),
+       ("c'exportWave", "ExportWave_", "rl_bindings.h", [t|Ptr Wave -> CString -> IO CBool|]),
+       ("c'exportWaveAsCode", "ExportWaveAsCode_", "rl_bindings.h", [t|Ptr Wave -> CString -> IO CBool|]),
+       ("c'playSound", "PlaySound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|]),
+       ("c'stopSound", "StopSound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|]),
+       ("c'pauseSound", "PauseSound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|]),
+       ("c'resumeSound", "ResumeSound_", "rl_bindings.h", [t|Ptr Sound -> IO ()|]),
+       ("c'isSoundPlaying", "IsSoundPlaying_", "rl_bindings.h", [t|Ptr Sound -> IO CBool|]),
+       ("c'setSoundVolume", "SetSoundVolume_", "rl_bindings.h", [t|Ptr Sound -> CFloat -> IO ()|]),
+       ("c'setSoundPitch", "SetSoundPitch_", "rl_bindings.h", [t|Ptr Sound -> CFloat -> IO ()|]),
+       ("c'setSoundPan", "SetSoundPan_", "rl_bindings.h", [t|Ptr Sound -> CFloat -> IO ()|]),
+       ("c'waveCopy", "WaveCopy_", "rl_bindings.h", [t|Ptr Wave -> IO (Ptr Wave)|]),
+       ("c'waveCrop", "WaveCrop_", "rl_bindings.h", [t|Ptr Wave -> CInt -> CInt -> IO ()|]),
+       ("c'waveFormat", "WaveFormat_", "rl_bindings.h", [t|Ptr Wave -> CInt -> CInt -> CInt -> IO ()|]),
+       ("c'loadWaveSamples", "LoadWaveSamples_", "rl_bindings.h", [t|Ptr Wave -> IO (Ptr CFloat)|]),
+       ("c'unloadWaveSamples", "UnloadWaveSamples_", "rl_bindings.h", [t|Ptr CFloat -> IO ()|]),
+       ("c'loadMusicStream", "LoadMusicStream_", "rl_bindings.h", [t|CString -> IO (Ptr Music)|]),
+       ("c'loadMusicStreamFromMemory", "LoadMusicStreamFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> IO (Ptr Music)|]),
+       ("c'isMusicReady", "IsMusicReady_", "rl_bindings.h", [t|Ptr Music -> IO CBool|]),
+       ("c'unloadMusicStream", "UnloadMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|]),
+       ("c'playMusicStream", "PlayMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|]),
+       ("c'isMusicStreamPlaying", "IsMusicStreamPlaying_", "rl_bindings.h", [t|Ptr Music -> IO CBool|]),
+       ("c'updateMusicStream", "UpdateMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|]),
+       ("c'stopMusicStream", "StopMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|]),
+       ("c'pauseMusicStream", "PauseMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|]),
+       ("c'resumeMusicStream", "ResumeMusicStream_", "rl_bindings.h", [t|Ptr Music -> IO ()|]),
+       ("c'seekMusicStream", "SeekMusicStream_", "rl_bindings.h", [t|Ptr Music -> CFloat -> IO ()|]),
+       ("c'setMusicVolume", "SetMusicVolume_", "rl_bindings.h", [t|Ptr Music -> CFloat -> IO ()|]),
+       ("c'setMusicPitch", "SetMusicPitch_", "rl_bindings.h", [t|Ptr Music -> CFloat -> IO ()|]),
+       ("c'setMusicPan", "SetMusicPan_", "rl_bindings.h", [t|Ptr Music -> CFloat -> IO ()|]),
+       ("c'getMusicTimeLength", "GetMusicTimeLength_", "rl_bindings.h", [t|Ptr Music -> IO CFloat|]),
+       ("c'getMusicTimePlayed", "GetMusicTimePlayed_", "rl_bindings.h", [t|Ptr Music -> IO CFloat|]),
+       ("c'loadAudioStream", "LoadAudioStream_", "rl_bindings.h", [t|CUInt -> CUInt -> CUInt -> IO (Ptr AudioStream)|]),
+       ("c'isAudioStreamReady", "IsAudioStreamReady_", "rl_bindings.h", [t|Ptr AudioStream -> IO CBool|]),
+       ("c'unloadAudioStream", "UnloadAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|]),
+       ("c'updateAudioStream", "UpdateAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> Ptr () -> CInt -> IO ()|]),
+       ("c'isAudioStreamProcessed", "IsAudioStreamProcessed_", "rl_bindings.h", [t|Ptr AudioStream -> IO CBool|]),
+       ("c'playAudioStream", "PlayAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|]),
+       ("c'pauseAudioStream", "PauseAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|]),
+       ("c'resumeAudioStream", "ResumeAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|]),
+       ("c'isAudioStreamPlaying", "IsAudioStreamPlaying_", "rl_bindings.h", [t|Ptr AudioStream -> IO CBool|]),
+       ("c'stopAudioStream", "StopAudioStream_", "rl_bindings.h", [t|Ptr AudioStream -> IO ()|]),
+       ("c'setAudioStreamVolume", "SetAudioStreamVolume_", "rl_bindings.h", [t|Ptr AudioStream -> CFloat -> IO ()|]),
+       ("c'setAudioStreamPitch", "SetAudioStreamPitch_", "rl_bindings.h", [t|Ptr AudioStream -> CFloat -> IO ()|]),
+       ("c'setAudioStreamPan", "SetAudioStreamPan_", "rl_bindings.h", [t|Ptr AudioStream -> CFloat -> IO ()|]),
+       ("c'setAudioStreamBufferSizeDefault", "SetAudioStreamBufferSizeDefault_", "rl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'setAudioStreamCallback", "SetAudioStreamCallback_", "rl_bindings.h", [t|Ptr AudioStream -> C'AudioCallback -> IO ()|]),
+       ("c'attachAudioStreamProcessor", "AttachAudioStreamProcessor_", "rl_bindings.h", [t|Ptr AudioStream -> C'AudioCallback -> IO ()|]),
+       ("c'detachAudioStreamProcessor", "DetachAudioStreamProcessor_", "rl_bindings.h", [t|Ptr AudioStream -> C'AudioCallback -> IO ()|]),
+       ("c'attachAudioMixedProcessor", "AttachAudioMixedProcessor_", "rl_bindings.h", [t|C'AudioCallback -> IO ()|]),
+       ("c'detachAudioMixedProcessor", "DetachAudioMixedProcessor_", "rl_bindings.h", [t|C'AudioCallback -> IO ()|])
      ]
  )
 
diff --git a/src/Raylib/Core/Camera.hs b/src/Raylib/Core/Camera.hs
--- a/src/Raylib/Core/Camera.hs
+++ b/src/Raylib/Core/Camera.hs
@@ -20,8 +20,8 @@
 import Raylib.Types (Camera3D, CameraMode, Vector3)
 
 $( genNative
-     [ ("c'updateCamera", "UpdateCamera_", "rl_bindings.h", [t|Ptr Camera3D -> CInt -> IO ()|], False),
-       ("c'updateCameraPro", "UpdateCameraPro_", "rl_bindings.h", [t|Ptr Camera3D -> Ptr Vector3 -> Ptr Vector3 -> CFloat -> IO ()|], False)
+     [ ("c'updateCamera", "UpdateCamera_", "rl_bindings.h", [t|Ptr Camera3D -> CInt -> IO ()|]),
+       ("c'updateCameraPro", "UpdateCameraPro_", "rl_bindings.h", [t|Ptr Camera3D -> Ptr Vector3 -> Ptr Vector3 -> CFloat -> IO ()|])
      ]
  )
 
diff --git a/src/Raylib/Core/Models.hs b/src/Raylib/Core/Models.hs
--- a/src/Raylib/Core/Models.hs
+++ b/src/Raylib/Core/Models.hs
@@ -34,6 +34,8 @@
     drawModelEx,
     drawModelWires,
     drawModelWiresEx,
+    drawModelPoints,
+    drawModelPointsEx,
     drawBoundingBox,
     drawBillboard,
     drawBillboardRec,
@@ -67,6 +69,7 @@
     loadModelAnimations,
     updateModelAnimation,
     isModelAnimationValid,
+    updateModelAnimationBoneMatrices,
     checkCollisionSpheres,
     checkCollisionBoxes,
     checkCollisionBoxSphere,
@@ -107,6 +110,8 @@
     c'drawModelEx,
     c'drawModelWires,
     c'drawModelWiresEx,
+    c'drawModelPoints,
+    c'drawModelPointsEx,
     c'drawBoundingBox,
     c'drawBillboard,
     c'drawBillboardRec,
@@ -142,6 +147,7 @@
     c'unloadModelAnimation,
     c'unloadModelAnimations,
     c'isModelAnimationValid,
+    c'updateModelAnimationBoneMatrices,
     c'checkCollisionSpheres,
     c'checkCollisionBoxes,
     c'checkCollisionBoxSphere,
@@ -193,79 +199,82 @@
   )
 
 $( genNative
-     [ ("c'drawLine3D", "DrawLine3D_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> Ptr Color -> IO ()|], False),
-       ("c'drawPoint3D", "DrawPoint3D_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Color -> IO ()|], False),
-       ("c'drawCircle3D", "DrawCircle3D_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawTriangle3D", "DrawTriangle3D_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> Ptr Vector3 -> Ptr Color -> IO ()|], False),
-       ("c'drawTriangleStrip3D", "DrawTriangleStrip3D_", "rl_bindings.h", [t|Ptr Vector3 -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawCube", "DrawCube_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawCubeV", "DrawCubeV_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> Ptr Color -> IO ()|], False),
-       ("c'drawCubeWires", "DrawCubeWires_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawCubeWiresV", "DrawCubeWiresV_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> Ptr Color -> IO ()|], False),
-       ("c'drawSphere", "DrawSphere_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSphereEx", "DrawSphereEx_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawSphereWires", "DrawSphereWires_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawCylinder", "DrawCylinder_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawCylinderEx", "DrawCylinderEx_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawCylinderWires", "DrawCylinderWires_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawCylinderWiresEx", "DrawCylinderWiresEx_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawCapsule", "DrawCapsule_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> CFloat -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawCapsuleWires", "DrawCapsuleWires_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> CFloat -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawPlane", "DrawPlane_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'drawRay", "DrawRay_", "rl_bindings.h", [t|Ptr Ray -> Ptr Color -> IO ()|], False),
-       ("c'drawGrid", "DrawGrid_", "rl_bindings.h", [t|CInt -> CFloat -> IO ()|], False),
-       ("c'loadModel", "LoadModel_", "rl_bindings.h", [t|CString -> IO (Ptr Model)|], False),
-       ("c'loadModelFromMesh", "LoadModelFromMesh_", "rl_bindings.h", [t|Ptr Mesh -> IO (Ptr Model)|], False),
-       ("c'isModelReady", "IsModelReady_", "rl_bindings.h", [t|Ptr Model -> IO CBool|], False),
-       ("c'unloadModel", "UnloadModel_", "rl_bindings.h", [t|Ptr Model -> IO ()|], False),
-       ("c'getModelBoundingBox", "GetModelBoundingBox_", "rl_bindings.h", [t|Ptr Model -> IO (Ptr BoundingBox)|], False),
-       ("c'drawModel", "DrawModel_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawModelEx", "DrawModelEx_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> Ptr Vector3 -> CFloat -> Ptr Vector3 -> Ptr Color -> IO ()|], False),
-       ("c'drawModelWires", "DrawModelWires_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawModelWiresEx", "DrawModelWiresEx_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> Ptr Vector3 -> CFloat -> Ptr Vector3 -> Ptr Color -> IO ()|], False),
-       ("c'drawBoundingBox", "DrawBoundingBox_", "rl_bindings.h", [t|Ptr BoundingBox -> Ptr Color -> IO ()|], False),
-       ("c'drawBillboard", "DrawBillboard_", "rl_bindings.h", [t|Ptr Camera3D -> Ptr Texture -> Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawBillboardRec", "DrawBillboardRec_", "rl_bindings.h", [t|Ptr Camera3D -> Ptr Texture -> Ptr Rectangle -> Ptr Vector3 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'drawBillboardPro", "DrawBillboardPro_", "rl_bindings.h", [t|Ptr Camera3D -> Ptr Texture -> Ptr Rectangle -> Ptr Vector3 -> Ptr Vector3 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'uploadMesh", "UploadMesh_", "rl_bindings.h", [t|Ptr Mesh -> CInt -> IO ()|], False),
-       ("c'updateMeshBuffer", "UpdateMeshBuffer_", "rl_bindings.h", [t|Ptr Mesh -> CInt -> Ptr () -> CInt -> CInt -> IO ()|], False),
-       ("c'unloadMesh", "UnloadMesh_", "rl_bindings.h", [t|Ptr Mesh -> IO ()|], False),
-       ("c'drawMesh", "DrawMesh_", "rl_bindings.h", [t|Ptr Mesh -> Ptr Material -> Ptr Matrix -> IO ()|], False),
-       ("c'drawMeshInstanced", "DrawMeshInstanced_", "rl_bindings.h", [t|Ptr Mesh -> Ptr Material -> Ptr Matrix -> CInt -> IO ()|], False),
-       ("c'exportMesh", "ExportMesh_", "rl_bindings.h", [t|Ptr Mesh -> CString -> IO CBool|], False),
-       ("c'exportMeshAsCode", "ExportMeshAsCode_", "rl_bindings.h", [t|Ptr Mesh -> CString -> IO CBool|], False),
-       ("c'getMeshBoundingBox", "GetMeshBoundingBox_", "rl_bindings.h", [t|Ptr Mesh -> IO (Ptr BoundingBox)|], False),
-       ("c'genMeshTangents", "GenMeshTangents_", "rl_bindings.h", [t|Ptr Mesh -> IO ()|], False),
-       ("c'genMeshPoly", "GenMeshPoly_", "rl_bindings.h", [t|CInt -> CFloat -> IO (Ptr Mesh)|], False),
-       ("c'genMeshPlane", "GenMeshPlane_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> CInt -> IO (Ptr Mesh)|], False),
-       ("c'genMeshCube", "GenMeshCube_", "rl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO (Ptr Mesh)|], False),
-       ("c'genMeshSphere", "GenMeshSphere_", "rl_bindings.h", [t|CFloat -> CInt -> CInt -> IO (Ptr Mesh)|], False),
-       ("c'genMeshHemiSphere", "GenMeshHemiSphere_", "rl_bindings.h", [t|CFloat -> CInt -> CInt -> IO (Ptr Mesh)|], False),
-       ("c'genMeshCylinder", "GenMeshCylinder_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> IO (Ptr Mesh)|], False),
-       ("c'genMeshCone", "GenMeshCone_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> IO (Ptr Mesh)|], False),
-       ("c'genMeshTorus", "GenMeshTorus_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> CInt -> IO (Ptr Mesh)|], False),
-       ("c'genMeshKnot", "GenMeshKnot_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> CInt -> IO (Ptr Mesh)|], False),
-       ("c'genMeshHeightmap", "GenMeshHeightmap_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector3 -> IO (Ptr Mesh)|], False),
-       ("c'genMeshCubicmap", "GenMeshCubicmap_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector3 -> IO (Ptr Mesh)|], False),
-       ("c'loadMaterials", "LoadMaterials_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr Material)|], False),
-       ("c'loadMaterialDefault", "LoadMaterialDefault_", "rl_bindings.h", [t|IO (Ptr Material)|], False),
-       ("c'isMaterialReady", "IsMaterialReady_", "rl_bindings.h", [t|Ptr Material -> IO CBool|], False),
-       ("c'unloadMaterial", "UnloadMaterial_", "rl_bindings.h", [t|Ptr Material -> IO ()|], False),
-       ("c'setMaterialTexture", "SetMaterialTexture_", "rl_bindings.h", [t|Ptr Material -> CInt -> Ptr Texture -> IO ()|], False),
-       ("c'setModelMeshMaterial", "SetModelMeshMaterial_", "rl_bindings.h", [t|Ptr Model -> CInt -> CInt -> IO ()|], False),
-       ("c'loadModelAnimations", "LoadModelAnimations_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr ModelAnimation)|], False),
-       ("c'updateModelAnimation", "UpdateModelAnimation_", "rl_bindings.h", [t|Ptr Model -> Ptr ModelAnimation -> CInt -> IO ()|], False),
-       ("c'unloadModelAnimation", "UnloadModelAnimation_", "rl_bindings.h", [t|Ptr ModelAnimation -> IO ()|], False),
-       ("c'unloadModelAnimations", "UnloadModelAnimations_", "rl_bindings.h", [t|Ptr ModelAnimation -> CInt -> IO ()|], False),
-       ("c'isModelAnimationValid", "IsModelAnimationValid_", "rl_bindings.h", [t|Ptr Model -> Ptr ModelAnimation -> IO CBool|], False),
-       ("c'checkCollisionSpheres", "CheckCollisionSpheres_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> Ptr Vector3 -> CFloat -> IO CBool|], False),
-       ("c'checkCollisionBoxes", "CheckCollisionBoxes_", "rl_bindings.h", [t|Ptr BoundingBox -> Ptr BoundingBox -> IO CBool|], False),
-       ("c'checkCollisionBoxSphere", "CheckCollisionBoxSphere_", "rl_bindings.h", [t|Ptr BoundingBox -> Ptr Vector3 -> CFloat -> IO CBool|], False),
-       ("c'getRayCollisionSphere", "GetRayCollisionSphere_", "rl_bindings.h", [t|Ptr Ray -> Ptr Vector3 -> CFloat -> IO (Ptr RayCollision)|], False),
-       ("c'getRayCollisionBox", "GetRayCollisionBox_", "rl_bindings.h", [t|Ptr Ray -> Ptr BoundingBox -> IO (Ptr RayCollision)|], False),
-       ("c'getRayCollisionMesh", "GetRayCollisionMesh_", "rl_bindings.h", [t|Ptr Ray -> Ptr Mesh -> Ptr Matrix -> IO (Ptr RayCollision)|], False),
-       ("c'getRayCollisionTriangle", "GetRayCollisionTriangle_", "rl_bindings.h", [t|Ptr Ray -> Ptr Vector3 -> Ptr Vector3 -> Ptr Vector3 -> IO (Ptr RayCollision)|], False),
-       ("c'getRayCollisionQuad", "GetRayCollisionQuad_", "rl_bindings.h", [t|Ptr Ray -> Ptr Vector3 -> Ptr Vector3 -> Ptr Vector3 -> Ptr Vector3 -> IO (Ptr RayCollision)|], False)
+     [ ("c'drawLine3D", "DrawLine3D_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> Ptr Color -> IO ()|]),
+       ("c'drawPoint3D", "DrawPoint3D_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Color -> IO ()|]),
+       ("c'drawCircle3D", "DrawCircle3D_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawTriangle3D", "DrawTriangle3D_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> Ptr Vector3 -> Ptr Color -> IO ()|]),
+       ("c'drawTriangleStrip3D", "DrawTriangleStrip3D_", "rl_bindings.h", [t|Ptr Vector3 -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawCube", "DrawCube_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawCubeV", "DrawCubeV_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> Ptr Color -> IO ()|]),
+       ("c'drawCubeWires", "DrawCubeWires_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawCubeWiresV", "DrawCubeWiresV_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> Ptr Color -> IO ()|]),
+       ("c'drawSphere", "DrawSphere_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSphereEx", "DrawSphereEx_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawSphereWires", "DrawSphereWires_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawCylinder", "DrawCylinder_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawCylinderEx", "DrawCylinderEx_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawCylinderWires", "DrawCylinderWires_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawCylinderWiresEx", "DrawCylinderWiresEx_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawCapsule", "DrawCapsule_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> CFloat -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawCapsuleWires", "DrawCapsuleWires_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector3 -> CFloat -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawPlane", "DrawPlane_", "rl_bindings.h", [t|Ptr Vector3 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'drawRay", "DrawRay_", "rl_bindings.h", [t|Ptr Ray -> Ptr Color -> IO ()|]),
+       ("c'drawGrid", "DrawGrid_", "rl_bindings.h", [t|CInt -> CFloat -> IO ()|]),
+       ("c'loadModel", "LoadModel_", "rl_bindings.h", [t|CString -> IO (Ptr Model)|]),
+       ("c'loadModelFromMesh", "LoadModelFromMesh_", "rl_bindings.h", [t|Ptr Mesh -> IO (Ptr Model)|]),
+       ("c'isModelReady", "IsModelReady_", "rl_bindings.h", [t|Ptr Model -> IO CBool|]),
+       ("c'unloadModel", "UnloadModel_", "rl_bindings.h", [t|Ptr Model -> IO ()|]),
+       ("c'getModelBoundingBox", "GetModelBoundingBox_", "rl_bindings.h", [t|Ptr Model -> IO (Ptr BoundingBox)|]),
+       ("c'drawModel", "DrawModel_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawModelEx", "DrawModelEx_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> Ptr Vector3 -> CFloat -> Ptr Vector3 -> Ptr Color -> IO ()|]),
+       ("c'drawModelWires", "DrawModelWires_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawModelWiresEx", "DrawModelWiresEx_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> Ptr Vector3 -> CFloat -> Ptr Vector3 -> Ptr Color -> IO ()|]),
+       ("c'drawModelPoints", "DrawModelPoints_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawModelPointsEx", "DrawModelPointsEx_", "rl_bindings.h", [t|Ptr Model -> Ptr Vector3 -> Ptr Vector3 -> CFloat -> Ptr Vector3 -> Ptr Color -> IO ()|]),
+       ("c'drawBoundingBox", "DrawBoundingBox_", "rl_bindings.h", [t|Ptr BoundingBox -> Ptr Color -> IO ()|]),
+       ("c'drawBillboard", "DrawBillboard_", "rl_bindings.h", [t|Ptr Camera3D -> Ptr Texture -> Ptr Vector3 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawBillboardRec", "DrawBillboardRec_", "rl_bindings.h", [t|Ptr Camera3D -> Ptr Texture -> Ptr Rectangle -> Ptr Vector3 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'drawBillboardPro", "DrawBillboardPro_", "rl_bindings.h", [t|Ptr Camera3D -> Ptr Texture -> Ptr Rectangle -> Ptr Vector3 -> Ptr Vector3 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'uploadMesh", "UploadMesh_", "rl_bindings.h", [t|Ptr Mesh -> CInt -> IO ()|]),
+       ("c'updateMeshBuffer", "UpdateMeshBuffer_", "rl_bindings.h", [t|Ptr Mesh -> CInt -> Ptr () -> CInt -> CInt -> IO ()|]),
+       ("c'unloadMesh", "UnloadMesh_", "rl_bindings.h", [t|Ptr Mesh -> IO ()|]),
+       ("c'drawMesh", "DrawMesh_", "rl_bindings.h", [t|Ptr Mesh -> Ptr Material -> Ptr Matrix -> IO ()|]),
+       ("c'drawMeshInstanced", "DrawMeshInstanced_", "rl_bindings.h", [t|Ptr Mesh -> Ptr Material -> Ptr Matrix -> CInt -> IO ()|]),
+       ("c'exportMesh", "ExportMesh_", "rl_bindings.h", [t|Ptr Mesh -> CString -> IO CBool|]),
+       ("c'exportMeshAsCode", "ExportMeshAsCode_", "rl_bindings.h", [t|Ptr Mesh -> CString -> IO CBool|]),
+       ("c'getMeshBoundingBox", "GetMeshBoundingBox_", "rl_bindings.h", [t|Ptr Mesh -> IO (Ptr BoundingBox)|]),
+       ("c'genMeshTangents", "GenMeshTangents_", "rl_bindings.h", [t|Ptr Mesh -> IO ()|]),
+       ("c'genMeshPoly", "GenMeshPoly_", "rl_bindings.h", [t|CInt -> CFloat -> IO (Ptr Mesh)|]),
+       ("c'genMeshPlane", "GenMeshPlane_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> CInt -> IO (Ptr Mesh)|]),
+       ("c'genMeshCube", "GenMeshCube_", "rl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO (Ptr Mesh)|]),
+       ("c'genMeshSphere", "GenMeshSphere_", "rl_bindings.h", [t|CFloat -> CInt -> CInt -> IO (Ptr Mesh)|]),
+       ("c'genMeshHemiSphere", "GenMeshHemiSphere_", "rl_bindings.h", [t|CFloat -> CInt -> CInt -> IO (Ptr Mesh)|]),
+       ("c'genMeshCylinder", "GenMeshCylinder_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> IO (Ptr Mesh)|]),
+       ("c'genMeshCone", "GenMeshCone_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> IO (Ptr Mesh)|]),
+       ("c'genMeshTorus", "GenMeshTorus_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> CInt -> IO (Ptr Mesh)|]),
+       ("c'genMeshKnot", "GenMeshKnot_", "rl_bindings.h", [t|CFloat -> CFloat -> CInt -> CInt -> IO (Ptr Mesh)|]),
+       ("c'genMeshHeightmap", "GenMeshHeightmap_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector3 -> IO (Ptr Mesh)|]),
+       ("c'genMeshCubicmap", "GenMeshCubicmap_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector3 -> IO (Ptr Mesh)|]),
+       ("c'loadMaterials", "LoadMaterials_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr Material)|]),
+       ("c'loadMaterialDefault", "LoadMaterialDefault_", "rl_bindings.h", [t|IO (Ptr Material)|]),
+       ("c'isMaterialReady", "IsMaterialReady_", "rl_bindings.h", [t|Ptr Material -> IO CBool|]),
+       ("c'unloadMaterial", "UnloadMaterial_", "rl_bindings.h", [t|Ptr Material -> IO ()|]),
+       ("c'setMaterialTexture", "SetMaterialTexture_", "rl_bindings.h", [t|Ptr Material -> CInt -> Ptr Texture -> IO ()|]),
+       ("c'setModelMeshMaterial", "SetModelMeshMaterial_", "rl_bindings.h", [t|Ptr Model -> CInt -> CInt -> IO ()|]),
+       ("c'loadModelAnimations", "LoadModelAnimations_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr ModelAnimation)|]),
+       ("c'updateModelAnimation", "UpdateModelAnimation_", "rl_bindings.h", [t|Ptr Model -> Ptr ModelAnimation -> CInt -> IO ()|]),
+       ("c'unloadModelAnimation", "UnloadModelAnimation_", "rl_bindings.h", [t|Ptr ModelAnimation -> IO ()|]),
+       ("c'unloadModelAnimations", "UnloadModelAnimations_", "rl_bindings.h", [t|Ptr ModelAnimation -> CInt -> IO ()|]),
+       ("c'isModelAnimationValid", "IsModelAnimationValid_", "rl_bindings.h", [t|Ptr Model -> Ptr ModelAnimation -> IO CBool|]),
+       ("c'updateModelAnimationBoneMatrices", "UpdateModelAnimationBoneMatrices_", "rl_bindings.h", [t|Ptr Model -> Ptr ModelAnimation -> CInt -> IO ()|]),
+       ("c'checkCollisionSpheres", "CheckCollisionSpheres_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> Ptr Vector3 -> CFloat -> IO CBool|]),
+       ("c'checkCollisionBoxes", "CheckCollisionBoxes_", "rl_bindings.h", [t|Ptr BoundingBox -> Ptr BoundingBox -> IO CBool|]),
+       ("c'checkCollisionBoxSphere", "CheckCollisionBoxSphere_", "rl_bindings.h", [t|Ptr BoundingBox -> Ptr Vector3 -> CFloat -> IO CBool|]),
+       ("c'getRayCollisionSphere", "GetRayCollisionSphere_", "rl_bindings.h", [t|Ptr Ray -> Ptr Vector3 -> CFloat -> IO (Ptr RayCollision)|]),
+       ("c'getRayCollisionBox", "GetRayCollisionBox_", "rl_bindings.h", [t|Ptr Ray -> Ptr BoundingBox -> IO (Ptr RayCollision)|]),
+       ("c'getRayCollisionMesh", "GetRayCollisionMesh_", "rl_bindings.h", [t|Ptr Ray -> Ptr Mesh -> Ptr Matrix -> IO (Ptr RayCollision)|]),
+       ("c'getRayCollisionTriangle", "GetRayCollisionTriangle_", "rl_bindings.h", [t|Ptr Ray -> Ptr Vector3 -> Ptr Vector3 -> Ptr Vector3 -> IO (Ptr RayCollision)|]),
+       ("c'getRayCollisionQuad", "GetRayCollisionQuad_", "rl_bindings.h", [t|Ptr Ray -> Ptr Vector3 -> Ptr Vector3 -> Ptr Vector3 -> Ptr Vector3 -> IO (Ptr RayCollision)|])
      ]
  )
 
@@ -369,6 +378,12 @@
 drawModelWiresEx :: Model -> Vector3 -> Vector3 -> Float -> Vector3 -> Color -> IO ()
 drawModelWiresEx model position rotationAxis rotationAngle scale tint = withFreeable model (\m -> withFreeable position (\p -> withFreeable rotationAxis (\r -> withFreeable scale (withFreeable tint . c'drawModelWiresEx m p r (realToFrac rotationAngle)))))
 
+drawModelPoints :: Model -> Vector3 -> Float -> Color -> IO ()
+drawModelPoints model position scale tint = withFreeable model (\m -> withFreeable position (\p -> withFreeable tint (c'drawModelPoints m p (realToFrac scale))))
+
+drawModelPointsEx :: Model -> Vector3 -> Vector3 -> Float -> Vector3 -> Color -> IO ()
+drawModelPointsEx model position rotationAxis rotationAngle scale tint = withFreeable model (\m -> withFreeable position (\p -> withFreeable rotationAxis (\r -> withFreeable scale (withFreeable tint . c'drawModelPointsEx m p r (realToFrac rotationAngle)))))
+
 drawBoundingBox :: BoundingBox -> Color -> IO ()
 drawBoundingBox box color = withFreeable box (withFreeable color . c'drawBoundingBox)
 
@@ -498,6 +513,9 @@
 
 isModelAnimationValid :: Model -> ModelAnimation -> IO Bool
 isModelAnimationValid model animation = toBool <$> withFreeable model (withFreeable animation . c'isModelAnimationValid)
+
+updateModelAnimationBoneMatrices :: Model -> ModelAnimation -> Int -> IO ()
+updateModelAnimationBoneMatrices model animation frame = withFreeable model (\m -> withFreeable animation (\a -> c'updateModelAnimationBoneMatrices m a (fromIntegral frame)))
 
 checkCollisionSpheres :: Vector3 -> Float -> Vector3 -> Float -> Bool
 checkCollisionSpheres center1 radius1 center2 radius2 = toBool $ unsafePerformIO (withFreeable center1 (\c1 -> withFreeable center2 (\c2 -> c'checkCollisionSpheres c1 (realToFrac radius1) c2 (realToFrac radius2))))
diff --git a/src/Raylib/Core/Shapes.hs b/src/Raylib/Core/Shapes.hs
--- a/src/Raylib/Core/Shapes.hs
+++ b/src/Raylib/Core/Shapes.hs
@@ -154,72 +154,72 @@
 import Raylib.Types (Color, Rectangle, Texture, Vector2, pattern Vector2)
 
 $( genNative
-     [ ("c'setShapesTexture", "SetShapesTexture_", "rl_bindings.h", [t|Ptr Texture -> Ptr Rectangle -> IO ()|], False),
-       ("c'getShapesTexture", "GetShapesTexture_", "rl_bindings.h", [t|IO (Ptr Texture)|], False),
-       ("c'getShapesTextureRectangle", "GetShapesTextureRectangle_", "rl_bindings.h", [t|IO (Ptr Rectangle)|], False),
-       ("c'drawPixel", "DrawPixel_", "rl_bindings.h", [t|CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawPixelV", "DrawPixelV_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'drawLine", "DrawLine_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawLineV", "DrawLineV_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'drawLineEx", "DrawLineEx_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawLineStrip", "DrawLineStrip_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawLineBezier", "DrawLineBezier_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawCircle", "DrawCircle_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawCircleSector", "DrawCircleSector_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawCircleSectorLines", "DrawCircleSectorLines_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawCircleGradient", "DrawCircleGradient_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> Ptr Color -> IO ()|], False),
-       ("c'drawCircleV", "DrawCircleV_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawCircleLines", "DrawCircleLines_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawCircleLinesV", "DrawCircleLinesV_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawEllipse", "DrawEllipse_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawEllipseLines", "DrawEllipseLines_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawRing", "DrawRing_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawRingLines", "DrawRingLines_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangle", "DrawRectangle_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleV", "DrawRectangleV_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleRec", "DrawRectangleRec_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Color -> IO ()|], False),
-       ("c'drawRectanglePro", "DrawRectanglePro_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleGradientV", "DrawRectangleGradientV_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleGradientH", "DrawRectangleGradientH_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleGradientEx", "DrawRectangleGradientEx_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Color -> Ptr Color -> Ptr Color -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleLines", "DrawRectangleLines_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleLinesEx", "DrawRectangleLinesEx_", "rl_bindings.h", [t|Ptr Rectangle -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleRounded", "DrawRectangleRounded_", "rl_bindings.h", [t|Ptr Rectangle -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleRoundedLines", "DrawRectangleRoundedLines_", "rl_bindings.h", [t|Ptr Rectangle -> CFloat -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawRectangleRoundedLinesEx", "DrawRectangleRoundedLinesEx_", "rl_bindings.h", [t|Ptr Rectangle -> CFloat -> CInt -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawTriangle", "DrawTriangle_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'drawTriangleLines", "DrawTriangleLines_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'drawTriangleFan", "DrawTriangleFan_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawTriangleStrip", "DrawTriangleStrip_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawPoly", "DrawPoly_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawPolyLines", "DrawPolyLines_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawPolyLinesEx", "DrawPolyLinesEx_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineLinear", "DrawSplineLinear_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineBasis", "DrawSplineBasis_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineCatmullRom", "DrawSplineCatmullRom_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineBezierQuadratic", "DrawSplineBezierQuadratic_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineBezierCubic", "DrawSplineBezierCubic_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineSegmentLinear", "DrawSplineSegmentLinear_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineSegmentBasis", "DrawSplineSegmentBasis_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineSegmentCatmullRom", "DrawSplineSegmentCatmullRom_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineSegmentBezierQuadratic", "DrawSplineSegmentBezierQuadratic_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawSplineSegmentBezierCubic", "DrawSplineSegmentBezierCubic_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'getSplinePointLinear", "GetSplinePointLinear_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|], False),
-       ("c'getSplinePointBasis", "GetSplinePointBasis_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|], False),
-       ("c'getSplinePointCatmullRom", "GetSplinePointCatmullRom_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|], False),
-       ("c'getSplinePointBezierQuad", "GetSplinePointBezierQuad_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|], False),
-       ("c'getSplinePointBezierCubic", "GetSplinePointBezierCubic_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|], False),
-       ("c'checkCollisionRecs", "CheckCollisionRecs_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Rectangle -> IO CBool|], False),
-       ("c'checkCollisionCircles", "CheckCollisionCircles_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Vector2 -> CFloat -> IO CBool|], False),
-       ("c'checkCollisionCircleRec", "CheckCollisionCircleRec_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Rectangle -> IO CBool|], False),
-       ("c'checkCollisionPointRec", "CheckCollisionPointRec_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Rectangle -> IO CBool|], False),
-       ("c'checkCollisionPointCircle", "CheckCollisionPointCircle_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO CBool|], False),
-       ("c'checkCollisionPointTriangle", "CheckCollisionPointTriangle_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> IO CBool|], False),
-       ("c'checkCollisionPointPoly", "CheckCollisionPointPoly_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CInt -> IO CBool|], False),
-       ("c'checkCollisionLines", "CheckCollisionLines_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> IO CBool|], False),
-       ("c'checkCollisionPointLine", "CheckCollisionPointLine_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CInt -> IO CBool|], False),
-       ("c'checkCollisionCircleLine", "CheckCollisionCircleLine_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Vector2 -> Ptr Vector2 -> IO CBool|], False),
-       ("c'getCollisionRec", "GetCollisionRec_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Rectangle -> IO (Ptr Rectangle)|], False)
+     [ ("c'setShapesTexture", "SetShapesTexture_", "rl_bindings.h", [t|Ptr Texture -> Ptr Rectangle -> IO ()|]),
+       ("c'getShapesTexture", "GetShapesTexture_", "rl_bindings.h", [t|IO (Ptr Texture)|]),
+       ("c'getShapesTextureRectangle", "GetShapesTextureRectangle_", "rl_bindings.h", [t|IO (Ptr Rectangle)|]),
+       ("c'drawPixel", "DrawPixel_", "rl_bindings.h", [t|CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawPixelV", "DrawPixelV_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'drawLine", "DrawLine_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawLineV", "DrawLineV_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'drawLineEx", "DrawLineEx_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawLineStrip", "DrawLineStrip_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawLineBezier", "DrawLineBezier_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawCircle", "DrawCircle_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawCircleSector", "DrawCircleSector_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawCircleSectorLines", "DrawCircleSectorLines_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawCircleGradient", "DrawCircleGradient_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> Ptr Color -> IO ()|]),
+       ("c'drawCircleV", "DrawCircleV_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawCircleLines", "DrawCircleLines_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawCircleLinesV", "DrawCircleLinesV_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawEllipse", "DrawEllipse_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawEllipseLines", "DrawEllipseLines_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawRing", "DrawRing_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawRingLines", "DrawRingLines_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawRectangle", "DrawRectangle_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleV", "DrawRectangleV_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleRec", "DrawRectangleRec_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Color -> IO ()|]),
+       ("c'drawRectanglePro", "DrawRectanglePro_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleGradientV", "DrawRectangleGradientV_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleGradientH", "DrawRectangleGradientH_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleGradientEx", "DrawRectangleGradientEx_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Color -> Ptr Color -> Ptr Color -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleLines", "DrawRectangleLines_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleLinesEx", "DrawRectangleLinesEx_", "rl_bindings.h", [t|Ptr Rectangle -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleRounded", "DrawRectangleRounded_", "rl_bindings.h", [t|Ptr Rectangle -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleRoundedLines", "DrawRectangleRoundedLines_", "rl_bindings.h", [t|Ptr Rectangle -> CFloat -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawRectangleRoundedLinesEx", "DrawRectangleRoundedLinesEx_", "rl_bindings.h", [t|Ptr Rectangle -> CFloat -> CInt -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawTriangle", "DrawTriangle_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'drawTriangleLines", "DrawTriangleLines_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'drawTriangleFan", "DrawTriangleFan_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawTriangleStrip", "DrawTriangleStrip_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawPoly", "DrawPoly_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawPolyLines", "DrawPolyLines_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawPolyLinesEx", "DrawPolyLinesEx_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineLinear", "DrawSplineLinear_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineBasis", "DrawSplineBasis_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineCatmullRom", "DrawSplineCatmullRom_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineBezierQuadratic", "DrawSplineBezierQuadratic_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineBezierCubic", "DrawSplineBezierCubic_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineSegmentLinear", "DrawSplineSegmentLinear_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineSegmentBasis", "DrawSplineSegmentBasis_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineSegmentCatmullRom", "DrawSplineSegmentCatmullRom_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineSegmentBezierQuadratic", "DrawSplineSegmentBezierQuadratic_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawSplineSegmentBezierCubic", "DrawSplineSegmentBezierCubic_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'getSplinePointLinear", "GetSplinePointLinear_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|]),
+       ("c'getSplinePointBasis", "GetSplinePointBasis_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|]),
+       ("c'getSplinePointCatmullRom", "GetSplinePointCatmullRom_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|]),
+       ("c'getSplinePointBezierQuad", "GetSplinePointBezierQuad_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|]),
+       ("c'getSplinePointBezierCubic", "GetSplinePointBezierCubic_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO (Ptr Vector2)|]),
+       ("c'checkCollisionRecs", "CheckCollisionRecs_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Rectangle -> IO CBool|]),
+       ("c'checkCollisionCircles", "CheckCollisionCircles_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Vector2 -> CFloat -> IO CBool|]),
+       ("c'checkCollisionCircleRec", "CheckCollisionCircleRec_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Rectangle -> IO CBool|]),
+       ("c'checkCollisionPointRec", "CheckCollisionPointRec_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Rectangle -> IO CBool|]),
+       ("c'checkCollisionPointCircle", "CheckCollisionPointCircle_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> IO CBool|]),
+       ("c'checkCollisionPointTriangle", "CheckCollisionPointTriangle_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> IO CBool|]),
+       ("c'checkCollisionPointPoly", "CheckCollisionPointPoly_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CInt -> IO CBool|]),
+       ("c'checkCollisionLines", "CheckCollisionLines_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> IO CBool|]),
+       ("c'checkCollisionPointLine", "CheckCollisionPointLine_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> CInt -> IO CBool|]),
+       ("c'checkCollisionCircleLine", "CheckCollisionCircleLine_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Vector2 -> Ptr Vector2 -> IO CBool|]),
+       ("c'getCollisionRec", "GetCollisionRec_", "rl_bindings.h", [t|Ptr Rectangle -> Ptr Rectangle -> IO (Ptr Rectangle)|])
      ]
  )
 
@@ -282,8 +282,8 @@
     )
 
 drawCircleGradient :: Int -> Int -> Float -> Color -> Color -> IO ()
-drawCircleGradient centerX centerY radius color1 color2 =
-  withFreeable color1 (withFreeable color2 . c'drawCircleGradient (fromIntegral centerX) (fromIntegral centerY) (realToFrac radius))
+drawCircleGradient centerX centerY radius inner outer =
+  withFreeable inner (withFreeable outer . c'drawCircleGradient (fromIntegral centerX) (fromIntegral centerY) (realToFrac radius))
 
 drawCircleV :: Vector2 -> Float -> Color -> IO ()
 drawCircleV center radius color =
@@ -354,10 +354,10 @@
   withFreeable color (\c -> withFreeable rect (\r -> withFreeable origin (\o -> c'drawRectanglePro r o (realToFrac rotation) c)))
 
 drawRectangleGradientV :: Int -> Int -> Int -> Int -> Color -> Color -> IO ()
-drawRectangleGradientV posX posY width height color1 color2 =
+drawRectangleGradientV posX posY width height top bottom =
   withFreeable
-    color1
-    ( withFreeable color2
+    top
+    ( withFreeable bottom
         . c'drawRectangleGradientV
           (fromIntegral posX)
           (fromIntegral posY)
@@ -366,10 +366,10 @@
     )
 
 drawRectangleGradientH :: Int -> Int -> Int -> Int -> Color -> Color -> IO ()
-drawRectangleGradientH posX posY width height color1 color2 =
+drawRectangleGradientH posX posY width height left right =
   withFreeable
-    color1
-    ( withFreeable color2
+    left
+    ( withFreeable right
         . c'drawRectangleGradientH
           (fromIntegral posX)
           (fromIntegral posY)
@@ -378,17 +378,17 @@
     )
 
 drawRectangleGradientEx :: Rectangle -> Color -> Color -> Color -> Color -> IO ()
-drawRectangleGradientEx rect col1 col2 col3 col4 =
+drawRectangleGradientEx rect topLeft bottomLeft topRight bottomRight =
   withFreeable
     rect
     ( \r ->
         withFreeable
-          col1
+          topLeft
           ( \c1 ->
               withFreeable
-                col2
+                bottomLeft
                 ( \c2 ->
-                    withFreeable col3 (withFreeable col4 . c'drawRectangleGradientEx r c1 c2)
+                    withFreeable topRight (withFreeable bottomRight . c'drawRectangleGradientEx r c1 c2)
                 )
           )
     )
diff --git a/src/Raylib/Core/Text.hs b/src/Raylib/Core/Text.hs
--- a/src/Raylib/Core/Text.hs
+++ b/src/Raylib/Core/Text.hs
@@ -98,35 +98,35 @@
   )
 
 $( genNative
-     [ ("c'getFontDefault", "GetFontDefault_", "rl_bindings.h", [t|IO (Ptr Font)|], False),
-       ("c'loadFont", "LoadFont_", "rl_bindings.h", [t|CString -> IO (Ptr Font)|], False),
-       ("c'loadFontEx", "LoadFontEx_", "rl_bindings.h", [t|CString -> CInt -> Ptr CInt -> CInt -> IO (Ptr Font)|], False),
-       ("c'loadFontFromImage", "LoadFontFromImage_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> CInt -> IO (Ptr Font)|], False),
-       ("c'loadFontFromMemory", "LoadFontFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> CInt -> Ptr CInt -> CInt -> IO (Ptr Font)|], False),
-       ("c'loadFontData", "LoadFontData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> CInt -> Ptr CInt -> CInt -> CInt -> IO (Ptr GlyphInfo)|], False),
-       ("c'genImageFontAtlas", "GenImageFontAtlas_", "rl_bindings.h", [t|Ptr GlyphInfo -> Ptr (Ptr Rectangle) -> CInt -> CInt -> CInt -> CInt -> IO (Ptr Image)|], False),
-       ("c'unloadFontData", "UnloadFontData_", "rl_bindings.h", [t|Ptr GlyphInfo -> CInt -> IO ()|], False),
-       ("c'isFontReady", "IsFontReady_", "rl_bindings.h", [t|Ptr Font -> IO CBool|], False),
-       ("c'unloadFont", "UnloadFont_", "rl_bindings.h", [t|Ptr Font -> IO ()|], False),
-       ("c'exportFontAsCode", "ExportFontAsCode_", "rl_bindings.h", [t|Ptr Font -> CString -> IO CBool|], False),
-       ("c'drawFPS", "DrawFPS_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'drawText", "DrawText_", "rl_bindings.h", [t|CString -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawTextEx", "DrawTextEx_", "rl_bindings.h", [t|Ptr Font -> CString -> Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawTextPro", "DrawTextPro_", "rl_bindings.h", [t|Ptr Font -> CString -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawTextCodepoint", "DrawTextCodepoint_", "rl_bindings.h", [t|Ptr Font -> CInt -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawTextCodepoints", "DrawTextCodepoints_", "rl_bindings.h", [t|Ptr Font -> Ptr CInt -> CInt -> Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'setTextLineSpacing", "SetTextLineSpacing_", "rl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'measureText", "MeasureText_", "rl_bindings.h", [t|CString -> CInt -> IO CInt|], False),
-       ("c'measureTextEx", "MeasureTextEx_", "rl_bindings.h", [t|Ptr Font -> CString -> CFloat -> CFloat -> IO (Ptr Vector2)|], False),
-       ("c'getGlyphIndex", "GetGlyphIndex_", "rl_bindings.h", [t|Ptr Font -> CInt -> IO CInt|], False),
-       ("c'getGlyphInfo", "GetGlyphInfo_", "rl_bindings.h", [t|Ptr Font -> CInt -> IO (Ptr GlyphInfo)|], False),
-       ("c'getGlyphAtlasRec", "GetGlyphAtlasRec_", "rl_bindings.h", [t|Ptr Font -> CInt -> IO (Ptr Rectangle)|], False),
-       ("c'loadUTF8", "LoadUTF8_", "rl_bindings.h", [t|Ptr CInt -> CInt -> IO CString|], False),
-       ("c'loadCodepoints", "LoadCodepoints_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr CInt)|], False),
-       ("c'getCodepointCount", "GetCodepointCount_", "rl_bindings.h", [t|CString -> IO CInt|], False),
-       ("c'getCodepointNext", "GetCodepointNext_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO CInt|], False),
-       ("c'getCodepointPrevious", "GetCodepointPrevious_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO CInt|], False),
-       ("c'codepointToUTF8", "CodepointToUTF8_", "rl_bindings.h", [t|CInt -> Ptr CInt -> IO CString|], False)
+     [ ("c'getFontDefault", "GetFontDefault_", "rl_bindings.h", [t|IO (Ptr Font)|]),
+       ("c'loadFont", "LoadFont_", "rl_bindings.h", [t|CString -> IO (Ptr Font)|]),
+       ("c'loadFontEx", "LoadFontEx_", "rl_bindings.h", [t|CString -> CInt -> Ptr CInt -> CInt -> IO (Ptr Font)|]),
+       ("c'loadFontFromImage", "LoadFontFromImage_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> CInt -> IO (Ptr Font)|]),
+       ("c'loadFontFromMemory", "LoadFontFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> CInt -> Ptr CInt -> CInt -> IO (Ptr Font)|]),
+       ("c'loadFontData", "LoadFontData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> CInt -> Ptr CInt -> CInt -> CInt -> IO (Ptr GlyphInfo)|]),
+       ("c'genImageFontAtlas", "GenImageFontAtlas_", "rl_bindings.h", [t|Ptr GlyphInfo -> Ptr (Ptr Rectangle) -> CInt -> CInt -> CInt -> CInt -> IO (Ptr Image)|]),
+       ("c'unloadFontData", "UnloadFontData_", "rl_bindings.h", [t|Ptr GlyphInfo -> CInt -> IO ()|]),
+       ("c'isFontReady", "IsFontReady_", "rl_bindings.h", [t|Ptr Font -> IO CBool|]),
+       ("c'unloadFont", "UnloadFont_", "rl_bindings.h", [t|Ptr Font -> IO ()|]),
+       ("c'exportFontAsCode", "ExportFontAsCode_", "rl_bindings.h", [t|Ptr Font -> CString -> IO CBool|]),
+       ("c'drawFPS", "DrawFPS_", "rl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'drawText", "DrawText_", "rl_bindings.h", [t|CString -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawTextEx", "DrawTextEx_", "rl_bindings.h", [t|Ptr Font -> CString -> Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawTextPro", "DrawTextPro_", "rl_bindings.h", [t|Ptr Font -> CString -> Ptr Vector2 -> Ptr Vector2 -> CFloat -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawTextCodepoint", "DrawTextCodepoint_", "rl_bindings.h", [t|Ptr Font -> CInt -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawTextCodepoints", "DrawTextCodepoints_", "rl_bindings.h", [t|Ptr Font -> Ptr CInt -> CInt -> Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'setTextLineSpacing", "SetTextLineSpacing_", "rl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'measureText", "MeasureText_", "rl_bindings.h", [t|CString -> CInt -> IO CInt|]),
+       ("c'measureTextEx", "MeasureTextEx_", "rl_bindings.h", [t|Ptr Font -> CString -> CFloat -> CFloat -> IO (Ptr Vector2)|]),
+       ("c'getGlyphIndex", "GetGlyphIndex_", "rl_bindings.h", [t|Ptr Font -> CInt -> IO CInt|]),
+       ("c'getGlyphInfo", "GetGlyphInfo_", "rl_bindings.h", [t|Ptr Font -> CInt -> IO (Ptr GlyphInfo)|]),
+       ("c'getGlyphAtlasRec", "GetGlyphAtlasRec_", "rl_bindings.h", [t|Ptr Font -> CInt -> IO (Ptr Rectangle)|]),
+       ("c'loadUTF8", "LoadUTF8_", "rl_bindings.h", [t|Ptr CInt -> CInt -> IO CString|]),
+       ("c'loadCodepoints", "LoadCodepoints_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr CInt)|]),
+       ("c'getCodepointCount", "GetCodepointCount_", "rl_bindings.h", [t|CString -> IO CInt|]),
+       ("c'getCodepointNext", "GetCodepointNext_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO CInt|]),
+       ("c'getCodepointPrevious", "GetCodepointPrevious_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO CInt|]),
+       ("c'codepointToUTF8", "CodepointToUTF8_", "rl_bindings.h", [t|CInt -> Ptr CInt -> IO CString|])
      ]
  )
 
diff --git a/src/Raylib/Core/Textures.hs b/src/Raylib/Core/Textures.hs
--- a/src/Raylib/Core/Textures.hs
+++ b/src/Raylib/Core/Textures.hs
@@ -5,7 +5,6 @@
   ( -- * High level
     loadImage,
     loadImageRaw,
-    loadImageSvg,
     loadImageAnim,
     loadImageAnimFromMemory,
     loadImageFromMemory,
@@ -108,6 +107,7 @@
     colorContrast,
     colorAlpha,
     colorAlphaBlend,
+    colorLerp,
     getColor,
     getPixelColor,
     setPixelColor,
@@ -116,7 +116,6 @@
     -- * Native
     c'loadImage,
     c'loadImageRaw,
-    c'loadImageSvg,
     c'loadImageAnim,
     c'loadImageAnimFromMemory,
     c'loadImageFromMemory,
@@ -221,6 +220,7 @@
     c'colorContrast,
     c'colorAlpha,
     c'colorAlphaBlend,
+    c'colorLerp,
     c'getColor,
     c'getPixelColor,
     c'setPixelColor,
@@ -272,116 +272,116 @@
   )
 
 $( genNative
-     [ ("c'loadImage", "LoadImage_", "rl_bindings.h", [t|CString -> IO (Ptr Image)|], False),
-       ("c'loadImageRaw", "LoadImageRaw_", "rl_bindings.h", [t|CString -> CInt -> CInt -> CInt -> CInt -> IO (Ptr Image)|], False),
-       ("c'loadImageSvg", "LoadImageSvg_", "rl_bindings.h", [t|CString -> CInt -> CInt -> IO (Ptr Image)|], False),
-       ("c'loadImageAnim", "LoadImageAnim_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr Image)|], False),
-       ("c'loadImageAnimFromMemory", "LoadImageAnimFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> Ptr CInt -> IO (Ptr Image)|], False),
-       ("c'loadImageFromMemory", "LoadImageFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> IO (Ptr Image)|], False),
-       ("c'loadImageFromTexture", "LoadImageFromTexture_", "rl_bindings.h", [t|Ptr Texture -> IO (Ptr Image)|], False),
-       ("c'loadImageFromScreen", "LoadImageFromScreen_", "rl_bindings.h", [t|IO (Ptr Image)|], False),
-       ("c'isImageReady", "IsImageReady_", "rl_bindings.h", [t|Ptr Image -> IO CBool|], False),
-       ("c'unloadImage", "UnloadImage_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'exportImage", "ExportImage_", "rl_bindings.h", [t|Ptr Image -> CString -> IO CBool|], False),
-       ("c'exportImageToMemory", "ExportImageToMemory_", "rl_bindings.h", [t|Ptr Image -> CString -> Ptr CInt -> IO (Ptr CUChar)|], False),
-       ("c'exportImageAsCode", "ExportImageAsCode_", "rl_bindings.h", [t|Ptr Image -> CString -> IO CBool|], False),
-       ("c'genImageColor", "GenImageColor_", "rl_bindings.h", [t|CInt -> CInt -> Ptr Color -> IO (Ptr Image)|], False),
-       ("c'genImageGradientLinear", "GenImageGradientLinear_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> Ptr Color -> Ptr Color -> IO (Ptr Image)|], False),
-       ("c'genImageGradientRadial", "GenImageGradientRadial_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> Ptr Color -> IO (Ptr Image)|], False),
-       ("c'genImageGradientSquare", "GenImageGradientSquare_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> Ptr Color -> IO (Ptr Image)|], False),
-       ("c'genImageChecked", "GenImageChecked_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> Ptr Color -> IO (Ptr Image)|], False),
-       ("c'genImageWhiteNoise", "GenImageWhiteNoise_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> IO (Ptr Image)|], False),
-       ("c'genImagePerlinNoise", "GenImagePerlinNoise_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> CFloat -> IO (Ptr Image)|], False),
-       ("c'genImageCellular", "GenImageCellular_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> IO (Ptr Image)|], False),
-       ("c'genImageText", "GenImageText_", "rl_bindings.h", [t|CInt -> CInt -> CString -> IO (Ptr Image)|], False),
-       ("c'imageCopy", "ImageCopy_", "rl_bindings.h", [t|Ptr Image -> IO (Ptr Image)|], False),
-       ("c'imageFromImage", "ImageFromImage_", "rl_bindings.h", [t|Ptr Image -> Ptr Rectangle -> IO (Ptr Image)|], False),
-       ("c'imageFromChannel", "ImageFromChannel_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO (Ptr Image)|], False),
-       ("c'imageText", "ImageText_", "rl_bindings.h", [t|CString -> CInt -> Ptr Color -> IO (Ptr Image)|], False),
-       ("c'imageTextEx", "ImageTextEx_", "rl_bindings.h", [t|Ptr Font -> CString -> CFloat -> CFloat -> Ptr Color -> IO (Ptr Image)|], False),
-       ("c'imageFormat", "ImageFormat_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|], False),
-       ("c'imageToPOT", "ImageToPOT_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> IO ()|], False),
-       ("c'imageCrop", "ImageCrop_", "rl_bindings.h", [t|Ptr Image -> Ptr Rectangle -> IO ()|], False),
-       ("c'imageAlphaCrop", "ImageAlphaCrop_", "rl_bindings.h", [t|Ptr Image -> CFloat -> IO ()|], False),
-       ("c'imageAlphaClear", "ImageAlphaClear_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> CFloat -> IO ()|], False),
-       ("c'imageAlphaMask", "ImageAlphaMask_", "rl_bindings.h", [t|Ptr Image -> Ptr Image -> IO ()|], False),
-       ("c'imageAlphaPremultiply", "ImageAlphaPremultiply_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'imageBlurGaussian", "ImageBlurGaussian_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|], False),
-       ("c'imageKernelConvolution", "ImageKernelConvolution_", "rl_bindings.h", [t|Ptr Image -> Ptr CFloat -> CInt -> IO ()|], False),
-       ("c'imageResize", "ImageResize_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> IO ()|], False),
-       ("c'imageResizeNN", "ImageResizeNN_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> IO ()|], False),
-       ("c'imageResizeCanvas", "ImageResizeCanvas_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageMipmaps", "ImageMipmaps_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'imageDither", "ImageDither_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'imageFlipVertical", "ImageFlipVertical_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'imageFlipHorizontal", "ImageFlipHorizontal_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'imageRotate", "ImageRotate_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|], False),
-       ("c'imageRotateCW", "ImageRotateCW_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'imageRotateCCW", "ImageRotateCCW_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'imageColorTint", "ImageColorTint_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> IO ()|], False),
-       ("c'imageColorInvert", "ImageColorInvert_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'imageColorGrayscale", "ImageColorGrayscale_", "rl_bindings.h", [t|Ptr Image -> IO ()|], False),
-       ("c'imageColorContrast", "ImageColorContrast_", "rl_bindings.h", [t|Ptr Image -> CFloat -> IO ()|], False),
-       ("c'imageColorBrightness", "ImageColorBrightness_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|], False),
-       ("c'imageColorReplace", "ImageColorReplace_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> Ptr Color -> IO ()|], False),
-       ("c'loadImageColors", "LoadImageColors_", "rl_bindings.h", [t|Ptr Image -> IO (Ptr Color)|], False),
-       ("c'loadImagePalette", "LoadImagePalette_", "rl_bindings.h", [t|Ptr Image -> CInt -> Ptr CInt -> IO (Ptr Color)|], False),
-       ("c'getImageAlphaBorder", "GetImageAlphaBorder_", "rl_bindings.h", [t|Ptr Image -> CFloat -> IO (Ptr Rectangle)|], False),
-       ("c'getImageColor", "GetImageColor_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> IO (Ptr Color)|], False),
-       ("c'imageClearBackground", "ImageClearBackground_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawPixel", "ImageDrawPixel_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawPixelV", "ImageDrawPixelV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawLine", "ImageDrawLine_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawLineV", "ImageDrawLineV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawCircle", "ImageDrawCircle_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawCircleV", "ImageDrawCircleV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawCircleLines", "ImageDrawCircleLines_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawCircleLinesV", "ImageDrawCircleLinesV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawRectangle", "ImageDrawRectangle_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawRectangleV", "ImageDrawRectangleV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawRectangleRec", "ImageDrawRectangleRec_", "rl_bindings.h", [t|Ptr Image -> Ptr Rectangle -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawRectangleLines", "ImageDrawRectangleLines_", "rl_bindings.h", [t|Ptr Image -> Ptr Rectangle -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawTriangle", "ImageDrawTriangle_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawTriangleEx", "ImageDrawTriangleEx_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> Ptr Color -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawTriangleLines", "ImageDrawTriangleLines_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawTriangleFan", "ImageDrawTriangleFan_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawTriangleStrip", "ImageDrawTriangleStrip_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDraw", "ImageDraw_", "rl_bindings.h", [t|Ptr Image -> Ptr Image -> Ptr Rectangle -> Ptr Rectangle -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawText", "ImageDrawText_", "rl_bindings.h", [t|Ptr Image -> CString -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'imageDrawTextEx", "ImageDrawTextEx_", "rl_bindings.h", [t|Ptr Image -> Ptr Font -> CString -> Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'loadTexture", "LoadTexture_", "rl_bindings.h", [t|CString -> IO (Ptr Texture)|], False),
-       ("c'loadTextureFromImage", "LoadTextureFromImage_", "rl_bindings.h", [t|Ptr Image -> IO (Ptr Texture)|], False),
-       ("c'loadTextureCubemap", "LoadTextureCubemap_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO (Ptr Texture)|], False),
-       ("c'loadRenderTexture", "LoadRenderTexture_", "rl_bindings.h", [t|CInt -> CInt -> IO (Ptr RenderTexture)|], False),
-       ("c'isTextureReady", "IsTextureReady_", "rl_bindings.h", [t|Ptr Texture -> IO CBool|], False),
-       ("c'unloadTexture", "UnloadTexture_", "rl_bindings.h", [t|Ptr Texture -> IO ()|], False),
-       ("c'isRenderTextureReady", "IsRenderTextureReady_", "rl_bindings.h", [t|Ptr RenderTexture -> IO CBool|], False),
-       ("c'unloadRenderTexture", "UnloadRenderTexture_", "rl_bindings.h", [t|Ptr RenderTexture -> IO ()|], False),
-       ("c'updateTexture", "UpdateTexture_", "rl_bindings.h", [t|Ptr Texture -> Ptr () -> IO ()|], False),
-       ("c'updateTextureRec", "UpdateTextureRec_", "rl_bindings.h", [t|Ptr Texture -> Ptr Rectangle -> Ptr () -> IO ()|], False),
-       ("c'genTextureMipmaps", "GenTextureMipmaps_", "rl_bindings.h", [t|Ptr Texture -> IO ()|], False),
-       ("c'setTextureFilter", "SetTextureFilter_", "rl_bindings.h", [t|Ptr Texture -> CInt -> IO ()|], False),
-       ("c'setTextureWrap", "SetTextureWrap_", "rl_bindings.h", [t|Ptr Texture -> CInt -> IO ()|], False),
-       ("c'drawTexture", "DrawTexture_", "rl_bindings.h", [t|Ptr Texture -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'drawTextureV", "DrawTextureV_", "rl_bindings.h", [t|Ptr Texture -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'drawTextureEx", "DrawTextureEx_", "rl_bindings.h", [t|Ptr Texture -> Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawTextureRec", "DrawTextureRec_", "rl_bindings.h", [t|Ptr Texture -> Ptr Rectangle -> Ptr Vector2 -> Ptr Color -> IO ()|], False),
-       ("c'drawTexturePro", "DrawTexturePro_", "rl_bindings.h", [t|Ptr Texture -> Ptr Rectangle -> Ptr Rectangle -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'drawTextureNPatch", "DrawTextureNPatch_", "rl_bindings.h", [t|Ptr Texture -> Ptr NPatchInfo -> Ptr Rectangle -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|], False),
-       ("c'fade", "Fade_", "rl_bindings.h", [t|Ptr Color -> CFloat -> IO (Ptr Color)|], False),
-       ("c'colorToInt", "ColorToInt_", "rl_bindings.h", [t|Ptr Color -> IO CInt|], False),
-       ("c'colorNormalize", "ColorNormalize_", "rl_bindings.h", [t|Ptr Color -> IO (Ptr Vector4)|], False),
-       ("c'colorFromNormalized", "ColorFromNormalized_", "rl_bindings.h", [t|Ptr Vector4 -> IO (Ptr Color)|], False),
-       ("c'colorToHSV", "ColorToHSV_", "rl_bindings.h", [t|Ptr Color -> IO (Ptr Vector3)|], False),
-       ("c'colorFromHSV", "ColorFromHSV_", "rl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO (Ptr Color)|], False),
-       ("c'colorTint", "ColorTint_", "rl_bindings.h", [t|Ptr Color -> Ptr Color -> IO (Ptr Color)|], False),
-       ("c'colorBrightness", "ColorBrightness_", "rl_bindings.h", [t|Ptr Color -> CFloat -> IO (Ptr Color)|], False),
-       ("c'colorContrast", "ColorContrast_", "rl_bindings.h", [t|Ptr Color -> CFloat -> IO (Ptr Color)|], False),
-       ("c'colorAlpha", "ColorAlpha_", "rl_bindings.h", [t|Ptr Color -> CFloat -> IO (Ptr Color)|], False),
-       ("c'colorAlphaBlend", "ColorAlphaBlend_", "rl_bindings.h", [t|Ptr Color -> Ptr Color -> Ptr Color -> IO (Ptr Color)|], False),
-       ("c'getColor", "GetColor_", "rl_bindings.h", [t|CUInt -> IO (Ptr Color)|], False),
-       ("c'getPixelColor", "GetPixelColor_", "rl_bindings.h", [t|Ptr () -> CInt -> IO (Ptr Color)|], False),
-       ("c'setPixelColor", "SetPixelColor_", "rl_bindings.h", [t|Ptr () -> Ptr Color -> CInt -> IO ()|], False)
+     [ ("c'loadImage", "LoadImage_", "rl_bindings.h", [t|CString -> IO (Ptr Image)|]),
+       ("c'loadImageRaw", "LoadImageRaw_", "rl_bindings.h", [t|CString -> CInt -> CInt -> CInt -> CInt -> IO (Ptr Image)|]),
+       ("c'loadImageAnim", "LoadImageAnim_", "rl_bindings.h", [t|CString -> Ptr CInt -> IO (Ptr Image)|]),
+       ("c'loadImageAnimFromMemory", "LoadImageAnimFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> Ptr CInt -> IO (Ptr Image)|]),
+       ("c'loadImageFromMemory", "LoadImageFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> IO (Ptr Image)|]),
+       ("c'loadImageFromTexture", "LoadImageFromTexture_", "rl_bindings.h", [t|Ptr Texture -> IO (Ptr Image)|]),
+       ("c'loadImageFromScreen", "LoadImageFromScreen_", "rl_bindings.h", [t|IO (Ptr Image)|]),
+       ("c'isImageReady", "IsImageReady_", "rl_bindings.h", [t|Ptr Image -> IO CBool|]),
+       ("c'unloadImage", "UnloadImage_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'exportImage", "ExportImage_", "rl_bindings.h", [t|Ptr Image -> CString -> IO CBool|]),
+       ("c'exportImageToMemory", "ExportImageToMemory_", "rl_bindings.h", [t|Ptr Image -> CString -> Ptr CInt -> IO (Ptr CUChar)|]),
+       ("c'exportImageAsCode", "ExportImageAsCode_", "rl_bindings.h", [t|Ptr Image -> CString -> IO CBool|]),
+       ("c'genImageColor", "GenImageColor_", "rl_bindings.h", [t|CInt -> CInt -> Ptr Color -> IO (Ptr Image)|]),
+       ("c'genImageGradientLinear", "GenImageGradientLinear_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> Ptr Color -> Ptr Color -> IO (Ptr Image)|]),
+       ("c'genImageGradientRadial", "GenImageGradientRadial_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> Ptr Color -> IO (Ptr Image)|]),
+       ("c'genImageGradientSquare", "GenImageGradientSquare_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> Ptr Color -> IO (Ptr Image)|]),
+       ("c'genImageChecked", "GenImageChecked_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> Ptr Color -> IO (Ptr Image)|]),
+       ("c'genImageWhiteNoise", "GenImageWhiteNoise_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> IO (Ptr Image)|]),
+       ("c'genImagePerlinNoise", "GenImagePerlinNoise_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> CFloat -> IO (Ptr Image)|]),
+       ("c'genImageCellular", "GenImageCellular_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> IO (Ptr Image)|]),
+       ("c'genImageText", "GenImageText_", "rl_bindings.h", [t|CInt -> CInt -> CString -> IO (Ptr Image)|]),
+       ("c'imageCopy", "ImageCopy_", "rl_bindings.h", [t|Ptr Image -> IO (Ptr Image)|]),
+       ("c'imageFromImage", "ImageFromImage_", "rl_bindings.h", [t|Ptr Image -> Ptr Rectangle -> IO (Ptr Image)|]),
+       ("c'imageFromChannel", "ImageFromChannel_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO (Ptr Image)|]),
+       ("c'imageText", "ImageText_", "rl_bindings.h", [t|CString -> CInt -> Ptr Color -> IO (Ptr Image)|]),
+       ("c'imageTextEx", "ImageTextEx_", "rl_bindings.h", [t|Ptr Font -> CString -> CFloat -> CFloat -> Ptr Color -> IO (Ptr Image)|]),
+       ("c'imageFormat", "ImageFormat_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|]),
+       ("c'imageToPOT", "ImageToPOT_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> IO ()|]),
+       ("c'imageCrop", "ImageCrop_", "rl_bindings.h", [t|Ptr Image -> Ptr Rectangle -> IO ()|]),
+       ("c'imageAlphaCrop", "ImageAlphaCrop_", "rl_bindings.h", [t|Ptr Image -> CFloat -> IO ()|]),
+       ("c'imageAlphaClear", "ImageAlphaClear_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> CFloat -> IO ()|]),
+       ("c'imageAlphaMask", "ImageAlphaMask_", "rl_bindings.h", [t|Ptr Image -> Ptr Image -> IO ()|]),
+       ("c'imageAlphaPremultiply", "ImageAlphaPremultiply_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'imageBlurGaussian", "ImageBlurGaussian_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|]),
+       ("c'imageKernelConvolution", "ImageKernelConvolution_", "rl_bindings.h", [t|Ptr Image -> Ptr CFloat -> CInt -> IO ()|]),
+       ("c'imageResize", "ImageResize_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> IO ()|]),
+       ("c'imageResizeNN", "ImageResizeNN_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> IO ()|]),
+       ("c'imageResizeCanvas", "ImageResizeCanvas_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageMipmaps", "ImageMipmaps_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'imageDither", "ImageDither_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> CInt -> IO ()|]),
+       ("c'imageFlipVertical", "ImageFlipVertical_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'imageFlipHorizontal", "ImageFlipHorizontal_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'imageRotate", "ImageRotate_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|]),
+       ("c'imageRotateCW", "ImageRotateCW_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'imageRotateCCW", "ImageRotateCCW_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'imageColorTint", "ImageColorTint_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> IO ()|]),
+       ("c'imageColorInvert", "ImageColorInvert_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'imageColorGrayscale", "ImageColorGrayscale_", "rl_bindings.h", [t|Ptr Image -> IO ()|]),
+       ("c'imageColorContrast", "ImageColorContrast_", "rl_bindings.h", [t|Ptr Image -> CFloat -> IO ()|]),
+       ("c'imageColorBrightness", "ImageColorBrightness_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO ()|]),
+       ("c'imageColorReplace", "ImageColorReplace_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> Ptr Color -> IO ()|]),
+       ("c'loadImageColors", "LoadImageColors_", "rl_bindings.h", [t|Ptr Image -> IO (Ptr Color)|]),
+       ("c'loadImagePalette", "LoadImagePalette_", "rl_bindings.h", [t|Ptr Image -> CInt -> Ptr CInt -> IO (Ptr Color)|]),
+       ("c'getImageAlphaBorder", "GetImageAlphaBorder_", "rl_bindings.h", [t|Ptr Image -> CFloat -> IO (Ptr Rectangle)|]),
+       ("c'getImageColor", "GetImageColor_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> IO (Ptr Color)|]),
+       ("c'imageClearBackground", "ImageClearBackground_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> IO ()|]),
+       ("c'imageDrawPixel", "ImageDrawPixel_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawPixelV", "ImageDrawPixelV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'imageDrawLine", "ImageDrawLine_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawLineV", "ImageDrawLineV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'imageDrawCircle", "ImageDrawCircle_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawCircleV", "ImageDrawCircleV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawCircleLines", "ImageDrawCircleLines_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawCircleLinesV", "ImageDrawCircleLinesV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawRectangle", "ImageDrawRectangle_", "rl_bindings.h", [t|Ptr Image -> CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawRectangleV", "ImageDrawRectangleV_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'imageDrawRectangleRec", "ImageDrawRectangleRec_", "rl_bindings.h", [t|Ptr Image -> Ptr Rectangle -> Ptr Color -> IO ()|]),
+       ("c'imageDrawRectangleLines", "ImageDrawRectangleLines_", "rl_bindings.h", [t|Ptr Image -> Ptr Rectangle -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawTriangle", "ImageDrawTriangle_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'imageDrawTriangleEx", "ImageDrawTriangleEx_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> Ptr Color -> Ptr Color -> IO ()|]),
+       ("c'imageDrawTriangleLines", "ImageDrawTriangleLines_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> Ptr Vector2 -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'imageDrawTriangleFan", "ImageDrawTriangleFan_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawTriangleStrip", "ImageDrawTriangleStrip_", "rl_bindings.h", [t|Ptr Image -> Ptr Vector2 -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDraw", "ImageDraw_", "rl_bindings.h", [t|Ptr Image -> Ptr Image -> Ptr Rectangle -> Ptr Rectangle -> Ptr Color -> IO ()|]),
+       ("c'imageDrawText", "ImageDrawText_", "rl_bindings.h", [t|Ptr Image -> CString -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'imageDrawTextEx", "ImageDrawTextEx_", "rl_bindings.h", [t|Ptr Image -> Ptr Font -> CString -> Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'loadTexture", "LoadTexture_", "rl_bindings.h", [t|CString -> IO (Ptr Texture)|]),
+       ("c'loadTextureFromImage", "LoadTextureFromImage_", "rl_bindings.h", [t|Ptr Image -> IO (Ptr Texture)|]),
+       ("c'loadTextureCubemap", "LoadTextureCubemap_", "rl_bindings.h", [t|Ptr Image -> CInt -> IO (Ptr Texture)|]),
+       ("c'loadRenderTexture", "LoadRenderTexture_", "rl_bindings.h", [t|CInt -> CInt -> IO (Ptr RenderTexture)|]),
+       ("c'isTextureReady", "IsTextureReady_", "rl_bindings.h", [t|Ptr Texture -> IO CBool|]),
+       ("c'unloadTexture", "UnloadTexture_", "rl_bindings.h", [t|Ptr Texture -> IO ()|]),
+       ("c'isRenderTextureReady", "IsRenderTextureReady_", "rl_bindings.h", [t|Ptr RenderTexture -> IO CBool|]),
+       ("c'unloadRenderTexture", "UnloadRenderTexture_", "rl_bindings.h", [t|Ptr RenderTexture -> IO ()|]),
+       ("c'updateTexture", "UpdateTexture_", "rl_bindings.h", [t|Ptr Texture -> Ptr () -> IO ()|]),
+       ("c'updateTextureRec", "UpdateTextureRec_", "rl_bindings.h", [t|Ptr Texture -> Ptr Rectangle -> Ptr () -> IO ()|]),
+       ("c'genTextureMipmaps", "GenTextureMipmaps_", "rl_bindings.h", [t|Ptr Texture -> IO ()|]),
+       ("c'setTextureFilter", "SetTextureFilter_", "rl_bindings.h", [t|Ptr Texture -> CInt -> IO ()|]),
+       ("c'setTextureWrap", "SetTextureWrap_", "rl_bindings.h", [t|Ptr Texture -> CInt -> IO ()|]),
+       ("c'drawTexture", "DrawTexture_", "rl_bindings.h", [t|Ptr Texture -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'drawTextureV", "DrawTextureV_", "rl_bindings.h", [t|Ptr Texture -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'drawTextureEx", "DrawTextureEx_", "rl_bindings.h", [t|Ptr Texture -> Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawTextureRec", "DrawTextureRec_", "rl_bindings.h", [t|Ptr Texture -> Ptr Rectangle -> Ptr Vector2 -> Ptr Color -> IO ()|]),
+       ("c'drawTexturePro", "DrawTexturePro_", "rl_bindings.h", [t|Ptr Texture -> Ptr Rectangle -> Ptr Rectangle -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawTextureNPatch", "DrawTextureNPatch_", "rl_bindings.h", [t|Ptr Texture -> Ptr NPatchInfo -> Ptr Rectangle -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'fade", "Fade_", "rl_bindings.h", [t|Ptr Color -> CFloat -> IO (Ptr Color)|]),
+       ("c'colorToInt", "ColorToInt_", "rl_bindings.h", [t|Ptr Color -> IO CInt|]),
+       ("c'colorNormalize", "ColorNormalize_", "rl_bindings.h", [t|Ptr Color -> IO (Ptr Vector4)|]),
+       ("c'colorFromNormalized", "ColorFromNormalized_", "rl_bindings.h", [t|Ptr Vector4 -> IO (Ptr Color)|]),
+       ("c'colorToHSV", "ColorToHSV_", "rl_bindings.h", [t|Ptr Color -> IO (Ptr Vector3)|]),
+       ("c'colorFromHSV", "ColorFromHSV_", "rl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO (Ptr Color)|]),
+       ("c'colorTint", "ColorTint_", "rl_bindings.h", [t|Ptr Color -> Ptr Color -> IO (Ptr Color)|]),
+       ("c'colorBrightness", "ColorBrightness_", "rl_bindings.h", [t|Ptr Color -> CFloat -> IO (Ptr Color)|]),
+       ("c'colorContrast", "ColorContrast_", "rl_bindings.h", [t|Ptr Color -> CFloat -> IO (Ptr Color)|]),
+       ("c'colorAlpha", "ColorAlpha_", "rl_bindings.h", [t|Ptr Color -> CFloat -> IO (Ptr Color)|]),
+       ("c'colorAlphaBlend", "ColorAlphaBlend_", "rl_bindings.h", [t|Ptr Color -> Ptr Color -> Ptr Color -> IO (Ptr Color)|]),
+       ("c'colorLerp", "ColorLerp_", "rl_bindings.h", [t|Ptr Color -> Ptr Color -> CFloat -> IO (Ptr Color)|]),
+       ("c'getColor", "GetColor_", "rl_bindings.h", [t|CUInt -> IO (Ptr Color)|]),
+       ("c'getPixelColor", "GetPixelColor_", "rl_bindings.h", [t|Ptr () -> CInt -> IO (Ptr Color)|]),
+       ("c'setPixelColor", "SetPixelColor_", "rl_bindings.h", [t|Ptr () -> Ptr Color -> CInt -> IO ()|])
      ]
  )
 
@@ -392,9 +392,6 @@
 loadImageRaw fileName width height format headerSize =
   withCString fileName (\str -> c'loadImageRaw str (fromIntegral width) (fromIntegral height) (fromIntegral $ fromEnum format) (fromIntegral headerSize)) >>= pop
 
-loadImageSvg :: String -> Int -> Int -> IO Image
-loadImageSvg fileNameOrString width height = withCString fileNameOrString (\s -> c'loadImageSvg s (fromIntegral width) (fromIntegral height)) >>= pop
-
 -- | Returns the animation and the number of frames in a tuple
 loadImageAnim :: String -> IO (Image, Int)
 loadImageAnim fileName =
@@ -775,6 +772,9 @@
 
 colorAlphaBlend :: Color -> Color -> Color -> Color
 colorAlphaBlend dst src tint = unsafePerformIO $ withFreeable dst (\d -> withFreeable src (withFreeable tint . c'colorAlphaBlend d)) >>= pop
+
+colorLerp :: Color -> Color -> Float -> Color
+colorLerp color1 color2 factor = unsafePerformIO $ withFreeable color1 (\c1 -> withFreeable color2 (\c2 -> c'colorLerp c1 c2 (realToFrac factor))) >>= pop
 
 getColor :: Integer -> Color
 getColor hexValue = unsafePerformIO $ c'getColor (fromIntegral hexValue) >>= pop
diff --git a/src/Raylib/Internal.hs b/src/Raylib/Internal.hs
--- a/src/Raylib/Internal.hs
+++ b/src/Raylib/Internal.hs
@@ -150,16 +150,16 @@
       }
 
 $( genNative
-     [ ("c'rlGetShaderIdDefault", "rlGetShaderIdDefault_", "rlgl_bindings.h", [t|IO CUInt|], False),
-       ("c'rlUnloadShaderProgram", "rlUnloadShaderProgram_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlUnloadTexture", "rlUnloadTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlUnloadFramebuffer", "rlUnloadFramebuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlUnloadVertexArray", "rlUnloadVertexArray_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlUnloadVertexBuffer", "rlUnloadVertexBuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'unloadMusicStreamData", "UnloadMusicStreamData", "rl_internal.h", [t|CInt -> Ptr () -> IO ()|], False),
-       ("c'unloadAudioBuffer", "UnloadAudioBuffer_", "rl_internal.h", [t|Ptr () -> IO ()|], False),
-       ("c'unloadAudioBufferAlias", "UnloadAudioBufferAlias", "rl_internal.h", [t|Ptr () -> IO ()|], False),
-       ("c'getPixelDataSize", "GetPixelDataSize_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> IO CInt|], False)
+     [ ("c'rlGetShaderIdDefault", "rlGetShaderIdDefault_", "rlgl_bindings.h", [t|IO CUInt|]),
+       ("c'rlUnloadShaderProgram", "rlUnloadShaderProgram_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlUnloadTexture", "rlUnloadTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlUnloadFramebuffer", "rlUnloadFramebuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlUnloadVertexArray", "rlUnloadVertexArray_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlUnloadVertexBuffer", "rlUnloadVertexBuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'unloadMusicStreamData", "UnloadMusicStreamData", "rl_internal.h", [t|CInt -> Ptr () -> IO ()|]),
+       ("c'unloadAudioBuffer", "UnloadAudioBuffer_", "rl_internal.h", [t|Ptr () -> IO ()|]),
+       ("c'unloadAudioBufferAlias", "UnloadAudioBufferAlias", "rl_internal.h", [t|Ptr () -> IO ()|]),
+       ("c'getPixelDataSize", "GetPixelDataSize_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> IO CInt|])
      ]
  )
 
diff --git a/src/Raylib/Internal/TH.hs b/src/Raylib/Internal/TH.hs
--- a/src/Raylib/Internal/TH.hs
+++ b/src/Raylib/Internal/TH.hs
@@ -38,7 +38,7 @@
     TypeQ,
     mkName,
     nameBase,
-    reify, Foreign (ImportF), Callconv (CCall), Safety (Safe, Unsafe),
+    reify, Foreign (ImportF), Callconv (CCall), Safety (Safe),
   )
 
 #endif
@@ -60,16 +60,16 @@
 --   means @foreign import@ statements. On web platforms, this means
 --   `callRaylibFunction` calls.
 genNative ::
-  -- | (@hsName@, @cName@, @cFile@, @funType@, @isSafe@)
-  [(String, String, String, TypeQ, Bool)] ->
+  -- | (@hsName@, @cName@, @cFile@, @funType@)
+  [(String, String, String, TypeQ)] ->
   DecsQ
 genNative funs = do
-  funs' <- mapM (\(a, b, c, d, e) -> (a,b,c,,e) <$> d) funs
+  funs' <- mapM (\(a, b, c, d) -> (a,b,c,) <$> d) funs
   return (genNative' funs')
   where
     genNative' [] = []
 #ifdef WEB_FFI
-    genNative' ((hsName, cName, _, funType, _) : xs) =
+    genNative' ((hsName, cName, _, funType) : xs) =
       [ -- hsName :: funType
         SigD name funType,
         -- hsName = callRaylibFunction "_cName"
@@ -80,7 +80,7 @@
       where
         name = mkName hsName
 #else
-    genNative' ((hsName, cName, cFile, funType, isSafe) : xs) =
+    genNative' ((hsName, cName, cFile, funType) : xs) =
       -- foreign import ccall safe/unsafe "cFile cName" hsName :: funType
-      ForeignD (ImportF CCall (if isSafe then Safe else Unsafe) (cFile ++ " " ++ cName) (mkName hsName) funType) : genNative' xs
+      ForeignD (ImportF CCall Safe (cFile ++ " " ++ cName) (mkName hsName) funType) : genNative' xs
 #endif
diff --git a/src/Raylib/Types/Core.hs b/src/Raylib/Types/Core.hs
--- a/src/Raylib/Types/Core.hs
+++ b/src/Raylib/Types/Core.hs
@@ -106,10 +106,12 @@
     p'automationEventList'events,
 
     -- * Callbacks
+    TraceLogCallback,
     LoadFileDataCallback,
     SaveFileDataCallback,
     LoadFileTextCallback,
     SaveFileTextCallback,
+    C'TraceLogCallback,
     C'LoadFileDataCallback,
     C'SaveFileDataCallback,
     C'LoadFileTextCallback,
@@ -1153,6 +1155,8 @@
 -- core callbacks ---------------------
 ---------------------------------------
 
+type TraceLogCallback = TraceLogLevel -> String -> IO ()
+
 type LoadFileDataCallback = String -> IO [Integer]
 
 type SaveFileDataCallback a = String -> Ptr a -> Integer -> IO Bool
@@ -1160,6 +1164,8 @@
 type LoadFileTextCallback = String -> IO String
 
 type SaveFileTextCallback = String -> String -> IO Bool
+
+type C'TraceLogCallback = FunPtr (CInt -> CString -> IO ())
 
 type C'LoadFileDataCallback = FunPtr (CString -> Ptr CUInt -> IO (Ptr CUChar))
 
diff --git a/src/Raylib/Types/Core/Models.hs b/src/Raylib/Types/Core/Models.hs
--- a/src/Raylib/Types/Core/Models.hs
+++ b/src/Raylib/Types/Core/Models.hs
@@ -5,6 +5,7 @@
 module Raylib.Types.Core.Models
   ( -- * Enumerations
     MaterialMapIndex (..),
+    DefaultShaderAttributeLocation (..),
     ShaderLocationIndex (..),
     ShaderUniformDataType (..),
     ShaderUniformData (..),
@@ -134,6 +135,18 @@
   | MaterialMapBrdf
   deriving (Eq, Show, Enum)
 
+data DefaultShaderAttributeLocation
+  = DefaultShaderAttribLocationPosition
+  | DefaultShaderAttribLocationTexcoord
+  | DefaultShaderAttribLocationNormal
+  | DefaultShaderAttribLocationColor
+  | DefaultShaderAttribLocationTangent
+  | DefaultShaderAttribLocationTexcoord2
+  | DefaultShaderAttribLocationIndices
+  | DefaultShaderAttribLocationBoneIds
+  | DefaultShaderAttribLocationBoneWeights
+  deriving (Eq, Show, Enum)
+
 data ShaderLocationIndex
   = ShaderLocVertexPosition
   | ShaderLocVertexTexcoord01
@@ -161,6 +174,9 @@
   | ShaderLocMapIrradiance
   | ShaderLocMapPrefilter
   | ShaderLocMapBrdf
+  | ShaderLocVertexBoneIds
+  | ShaderLocVertexBoneWeights
+  | ShaderLocBoneMatrices
   deriving (Eq, Show, Enum)
 
 data ShaderUniformDataType
@@ -325,13 +341,16 @@
     mesh'animNormals :: Maybe [Vector3],
     mesh'boneIds :: Maybe [Word8],
     mesh'boneWeights :: Maybe [Float],
+    mesh'boneMatrices :: Maybe [Matrix],
+    mesh'boneCount :: Int,
     mesh'vaoId :: Integer,
+    -- | Use `toEnum` on `DefaultShaderAttributeLocation` for indices
     mesh'vboId :: Maybe [Integer]
   }
   deriving (Eq, Show)
 
 instance Storable Mesh where
-  sizeOf _ = 112
+  sizeOf _ = 120
   alignment _ = 8
   peek _p = do
     vertexCount <- fromIntegral <$> peek (p'mesh'vertexCount _p)
@@ -345,12 +364,14 @@
     indices <- (map fromIntegral <$>) <$> (peekMaybeArray vertexCount =<< peek (p'mesh'indices _p))
     animVertices <- peekMaybeArray vertexCount =<< peek (p'mesh'animVertices _p)
     animNormals <- peekMaybeArray vertexCount =<< peek (p'mesh'animNormals _p)
-    boneIds <- (map fromIntegral <$>) <$> (peekMaybeArray (vertexCount * 4) =<< peek (p'mesh'boneIds _p))
-    boneWeights <- (map realToFrac <$>) <$> (peekMaybeArray (vertexCount * 4) =<< peek (p'mesh'boneWeights _p))
+    boneCount <- fromIntegral <$> peek (p'mesh'boneCount _p)
+    boneIds <- (map fromIntegral <$>) <$> (peekMaybeArray boneCount =<< peek (p'mesh'boneIds _p))
+    boneWeights <- (map realToFrac <$>) <$> (peekMaybeArray boneCount =<< peek (p'mesh'boneWeights _p))
+    boneMatrices <- peekMaybeArray boneCount =<< peek (p'mesh'boneMatrices _p)
     vaoId <- fromIntegral <$> peek (p'mesh'vaoId _p)
-    vboId <- (map fromIntegral <$>) <$> (peekMaybeArray 7 =<< peek (p'mesh'vboId _p))
-    return $ Mesh vertexCount triangleCount vertices texcoords texcoords2 normals tangents colors indices animVertices animNormals boneIds boneWeights vaoId vboId
-  poke _p (Mesh vertexCount triangleCount vertices texcoords texcoords2 normals tangents colors indices animVertices animNormals boneIds boneWeights vaoId vboId) = do
+    vboId <- (map fromIntegral <$>) <$> (peekMaybeArray 9 =<< peek (p'mesh'vboId _p))
+    return $ Mesh vertexCount triangleCount vertices texcoords texcoords2 normals tangents colors indices animVertices animNormals boneIds boneWeights boneMatrices boneCount vaoId vboId
+  poke _p (Mesh vertexCount triangleCount vertices texcoords texcoords2 normals tangents colors indices animVertices animNormals boneIds boneWeights boneMatrices boneCount vaoId vboId) = do
     poke (p'mesh'vertexCount _p) (fromIntegral vertexCount)
     poke (p'mesh'triangleCount _p) (fromIntegral triangleCount)
     poke (p'mesh'vertices _p) =<< newArray vertices
@@ -364,6 +385,8 @@
     poke (p'mesh'animNormals _p) =<< newMaybeArray animNormals
     poke (p'mesh'boneIds _p) =<< newMaybeArray (map fromIntegral <$> boneIds)
     poke (p'mesh'boneWeights _p) =<< newMaybeArray (map realToFrac <$> boneWeights)
+    poke (p'mesh'boneMatrices _p) =<< newMaybeArray boneMatrices
+    poke (p'mesh'boneCount _p) (fromIntegral boneCount)
     poke (p'mesh'vaoId _p) (fromIntegral vaoId)
     poke (p'mesh'vboId _p) =<< newMaybeArray (map fromIntegral <$> vboId)
     return ()
@@ -420,20 +443,27 @@
 p'mesh'animNormals :: Ptr Mesh -> Ptr (Ptr Vector3)
 p'mesh'animNormals = (`plusPtr` 72)
 
--- maybe array (mesh'vertexCount * 4)
+-- maybe array (mesh'boneCount)
 p'mesh'boneIds :: Ptr Mesh -> Ptr (Ptr CUChar)
 p'mesh'boneIds = (`plusPtr` 80)
 
--- maybe array (mesh'vertexCount * 4)
+-- maybe array (mesh'boneCount)
 p'mesh'boneWeights :: Ptr Mesh -> Ptr (Ptr CFloat)
 p'mesh'boneWeights = (`plusPtr` 88)
 
+-- maybe array (mesh'boneCount)
+p'mesh'boneMatrices :: Ptr Mesh -> Ptr (Ptr Matrix)
+p'mesh'boneMatrices = (`plusPtr` 96)
+
+p'mesh'boneCount :: Ptr Mesh -> Ptr CInt
+p'mesh'boneCount = (`plusPtr` 104)
+
 p'mesh'vaoId :: Ptr Mesh -> Ptr CUInt
-p'mesh'vaoId = (`plusPtr` 96)
+p'mesh'vaoId = (`plusPtr` 108)
 
--- maybe array (7)
+-- maybe array (9)
 p'mesh'vboId :: Ptr Mesh -> Ptr (Ptr CUInt)
-p'mesh'vboId = (`plusPtr` 104)
+p'mesh'vboId = (`plusPtr` 112)
 
 instance Freeable Mesh where
   rlFreeDependents _ ptr = do
diff --git a/src/Raylib/Types/Util/GUI.hs b/src/Raylib/Types/Util/GUI.hs
--- a/src/Raylib/Types/Util/GUI.hs
+++ b/src/Raylib/Types/Util/GUI.hs
@@ -511,6 +511,8 @@
     DropdownItemsSpacing
   | -- | DropdownBox arrow hidden
     DropdownArrowHidden
+  | -- | DropdownBox roll up flag (default rolls down)
+    DropdownRollUp
   deriving (Eq, Show)
 
 instance Enum GuiDropdownBoxProperty where
@@ -518,10 +520,12 @@
     ArrowPadding -> 16
     DropdownItemsSpacing -> 17
     DropdownArrowHidden -> 18
+    DropdownRollUp -> 19
   toEnum x = case x of
     16 -> ArrowPadding
     17 -> DropdownItemsSpacing
     18 -> DropdownArrowHidden
+    19 -> DropdownRollUp
     n -> error $ "(GuiDropdownBoxProperty.toEnum) Invalid value: " ++ show n
 
 instance Storable GuiDropdownBoxProperty where
@@ -880,7 +884,7 @@
   | IconLayers2
   | IconMLayers
   | IconMaps
-  | Icon228
+  | IconHot
   | Icon229
   | Icon230
   | Icon231
@@ -1140,7 +1144,7 @@
     IconLayers2 -> 225
     IconMLayers -> 226
     IconMaps -> 227
-    Icon228 -> 228
+    IconHot -> 228
     Icon229 -> 229
     Icon230 -> 230
     Icon231 -> 231
@@ -1397,7 +1401,7 @@
     225 -> IconLayers2
     226 -> IconMLayers
     227 -> IconMaps
-    228 -> Icon228
+    228 -> IconHot
     229 -> Icon229
     230 -> Icon230
     231 -> Icon231
diff --git a/src/Raylib/Types/Util/RLGL.hs b/src/Raylib/Types/Util/RLGL.hs
--- a/src/Raylib/Types/Util/RLGL.hs
+++ b/src/Raylib/Types/Util/RLGL.hs
@@ -384,6 +384,14 @@
     RLShaderUniformIVec3
   | -- | Shader uniform type: ivec4 (4 int)
     RLShaderUniformIVec4
+  | -- | Shader uniform type: unsigned int
+    RLShaderUniformUInt
+  | -- | Shader uniform type: uivec2 (2 unsigned int)
+    RLShaderUniformUIVec2
+  | -- | Shader uniform type: uivec3 (3 unsigned int)
+    RLShaderUniformUIVec3
+  | -- | Shader uniform type: uivec4 (4 unsigned int)
+    RLShaderUniformUIVec4
   | -- | Shader uniform type: sampler2d
     RLShaderUniformSampler2D
   deriving (Eq, Show, Enum)
diff --git a/src/Raylib/Util/GUI.hs b/src/Raylib/Util/GUI.hs
--- a/src/Raylib/Util/GUI.hs
+++ b/src/Raylib/Util/GUI.hs
@@ -236,63 +236,63 @@
 import Raylib.Internal.TH (genNative)
 import Raylib.Types (Color (Color), Font, GuiControl (Default), GuiControlProperty (..), GuiDefaultProperty (..), GuiIconName, GuiState, GuiTextAlignment, GuiTextAlignmentVertical, GuiTextWrapMode, Rectangle (Rectangle), Vector2, pattern Vector2, Vector3, pattern Vector3)
 $( genNative
-     [ ("c'guiEnable", "GuiEnable_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiDisable", "GuiDisable_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLock", "GuiLock_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiUnlock", "GuiUnlock_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiIsLocked", "GuiIsLocked_", "rgui_bindings.h", [t|IO CBool|], False),
-       ("c'guiSetAlpha", "GuiSetAlpha_", "rgui_bindings.h", [t|CFloat -> IO ()|], False),
-       ("c'guiSetState", "GuiSetState_", "rgui_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'guiGetState", "GuiGetState_", "rgui_bindings.h", [t|IO CInt|], False),
-       ("c'guiSetFont", "GuiSetFont_", "rgui_bindings.h", [t|Ptr Font -> IO ()|], False),
-       ("c'guiGetFont", "GuiGetFont_", "rgui_bindings.h", [t|IO (Ptr Font)|], False),
-       ("c'guiSetStyle", "GuiSetStyle_", "rgui_bindings.h", [t|CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'guiGetStyle", "GuiGetStyle_", "rgui_bindings.h", [t|CInt -> CInt -> IO CInt|], False),
-       ("c'guiLoadStyle", "GuiLoadStyle_", "rgui_bindings.h", [t|CString -> IO ()|], False),
-       ("c'guiLoadStyleDefault", "GuiLoadStyleDefault_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiEnableTooltip", "GuiEnableTooltip_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiDisableTooltip", "GuiDisableTooltip_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiSetTooltip", "GuiSetTooltip_", "rgui_bindings.h", [t|CString -> IO ()|], False),
-       ("c'guiIconText", "GuiIconText_", "rgui_bindings.h", [t|CInt -> CString -> IO CString|], False),
-       ("c'guiSetIconScale", "GuiSetIconScale_", "rgui_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'guiGetIcons", "GuiGetIcons_", "rgui_bindings.h", [t|IO (Ptr CUInt)|], False),
-       ("c'guiLoadIcons", "GuiLoadIcons_", "rgui_bindings.h", [t|CString -> CBool -> IO (Ptr CString)|], False),
-       ("c'guiDrawIcon", "GuiDrawIcon_", "rgui_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|], False),
-       ("c'guiWindowBox", "GuiWindowBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|], False),
-       ("c'guiGroupBox", "GuiGroupBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|], False),
-       ("c'guiLine", "GuiLine_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|], False),
-       ("c'guiPanel", "GuiPanel_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|], False),
-       ("c'guiTabBar", "GuiTabBar_", "rgui_bindings.h", [t|Ptr Rectangle -> Ptr CString -> CInt -> Ptr CInt -> IO CInt|], False),
-       ("c'guiScrollPanel", "GuiScrollPanel_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Rectangle -> Ptr Vector2 -> Ptr Rectangle -> IO CInt|], False),
-       ("c'guiLabel", "GuiLabel_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|], False),
-       ("c'guiButton", "GuiButton_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|], False),
-       ("c'guiLabelButton", "GuiLabelButton_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|], False),
-       ("c'guiToggle", "GuiToggle_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CBool -> IO CInt|], False),
-       ("c'guiToggleGroup", "GuiToggleGroup_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> IO CInt|], False),
-       ("c'guiToggleSlider", "GuiToggleSlider_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> IO CInt|], False),
-       ("c'guiCheckBox", "GuiCheckBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CBool -> IO CInt|], False),
-       ("c'guiComboBox", "GuiComboBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> IO CInt|], False),
-       ("c'guiDropdownBox", "GuiDropdownBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> CBool -> IO CInt|], False),
-       ("c'guiSpinner", "GuiSpinner_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> CInt -> CInt -> CBool -> IO CInt|], False),
-       ("c'guiValueBox", "GuiValueBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> CInt -> CInt -> CBool -> IO CInt|], False),
-       ("c'guiValueBoxFloat", "GuiValueBoxFloat_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> Ptr CFloat -> CBool -> IO CInt|], False),
-       ("c'guiTextBox", "GuiTextBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CInt -> CBool -> IO CInt|], False),
-       ("c'guiSlider", "GuiSlider_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> Ptr CFloat -> CFloat -> CFloat -> IO CInt|], False),
-       ("c'guiSliderBar", "GuiSliderBar_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> Ptr CFloat -> CFloat -> CFloat -> IO CInt|], False),
-       ("c'guiProgressBar", "GuiProgressBar_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> Ptr CFloat -> CFloat -> CFloat -> IO CInt|], False),
-       ("c'guiStatusBar", "GuiStatusBar_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|], False),
-       ("c'guiDummyRec", "GuiDummyRec_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|], False),
-       ("c'guiGrid", "GuiGrid_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CFloat -> CInt -> Ptr Vector2 -> IO CInt|], False),
-       ("c'guiListView", "GuiListView_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> Ptr CInt -> IO CInt|], False),
-       ("c'guiListViewEx", "GuiListViewEx_", "rgui_bindings.h", [t|Ptr Rectangle -> Ptr CString -> CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO CInt|], False),
-       ("c'guiMessageBox", "GuiMessageBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> CString -> IO CInt|], False),
-       ("c'guiTextInputBox", "GuiTextInputBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> CString -> CString -> CInt -> Ptr CBool -> IO CInt|], False),
-       ("c'guiColorPicker", "GuiColorPicker_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Color -> IO CInt|], False),
-       ("c'guiColorPanel", "GuiColorPanel_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Color -> IO CInt|], False),
-       ("c'guiColorBarAlpha", "GuiColorBarAlpha_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CFloat -> IO CInt|], False),
-       ("c'guiColorBarHue", "GuiColorBarHue_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CFloat -> IO CInt|], False),
-       ("c'guiColorPickerHSV", "GuiColorPickerHSV_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Vector3 -> IO CInt|], False),
-       ("c'guiColorPanelHSV", "GuiColorPanelHSV_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Vector3 -> IO CInt|], False)
+     [ ("c'guiEnable", "GuiEnable_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiDisable", "GuiDisable_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLock", "GuiLock_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiUnlock", "GuiUnlock_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiIsLocked", "GuiIsLocked_", "rgui_bindings.h", [t|IO CBool|]),
+       ("c'guiSetAlpha", "GuiSetAlpha_", "rgui_bindings.h", [t|CFloat -> IO ()|]),
+       ("c'guiSetState", "GuiSetState_", "rgui_bindings.h", [t|CInt -> IO ()|]),
+       ("c'guiGetState", "GuiGetState_", "rgui_bindings.h", [t|IO CInt|]),
+       ("c'guiSetFont", "GuiSetFont_", "rgui_bindings.h", [t|Ptr Font -> IO ()|]),
+       ("c'guiGetFont", "GuiGetFont_", "rgui_bindings.h", [t|IO (Ptr Font)|]),
+       ("c'guiSetStyle", "GuiSetStyle_", "rgui_bindings.h", [t|CInt -> CInt -> CInt -> IO ()|]),
+       ("c'guiGetStyle", "GuiGetStyle_", "rgui_bindings.h", [t|CInt -> CInt -> IO CInt|]),
+       ("c'guiLoadStyle", "GuiLoadStyle_", "rgui_bindings.h", [t|CString -> IO ()|]),
+       ("c'guiLoadStyleDefault", "GuiLoadStyleDefault_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiEnableTooltip", "GuiEnableTooltip_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiDisableTooltip", "GuiDisableTooltip_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiSetTooltip", "GuiSetTooltip_", "rgui_bindings.h", [t|CString -> IO ()|]),
+       ("c'guiIconText", "GuiIconText_", "rgui_bindings.h", [t|CInt -> CString -> IO CString|]),
+       ("c'guiSetIconScale", "GuiSetIconScale_", "rgui_bindings.h", [t|CInt -> IO ()|]),
+       ("c'guiGetIcons", "GuiGetIcons_", "rgui_bindings.h", [t|IO (Ptr CUInt)|]),
+       ("c'guiLoadIcons", "GuiLoadIcons_", "rgui_bindings.h", [t|CString -> CBool -> IO (Ptr CString)|]),
+       ("c'guiDrawIcon", "GuiDrawIcon_", "rgui_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'guiWindowBox", "GuiWindowBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
+       ("c'guiGroupBox", "GuiGroupBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
+       ("c'guiLine", "GuiLine_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
+       ("c'guiPanel", "GuiPanel_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
+       ("c'guiTabBar", "GuiTabBar_", "rgui_bindings.h", [t|Ptr Rectangle -> Ptr CString -> CInt -> Ptr CInt -> IO CInt|]),
+       ("c'guiScrollPanel", "GuiScrollPanel_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Rectangle -> Ptr Vector2 -> Ptr Rectangle -> IO CInt|]),
+       ("c'guiLabel", "GuiLabel_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
+       ("c'guiButton", "GuiButton_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
+       ("c'guiLabelButton", "GuiLabelButton_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
+       ("c'guiToggle", "GuiToggle_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CBool -> IO CInt|]),
+       ("c'guiToggleGroup", "GuiToggleGroup_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> IO CInt|]),
+       ("c'guiToggleSlider", "GuiToggleSlider_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> IO CInt|]),
+       ("c'guiCheckBox", "GuiCheckBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CBool -> IO CInt|]),
+       ("c'guiComboBox", "GuiComboBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> IO CInt|]),
+       ("c'guiDropdownBox", "GuiDropdownBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> CBool -> IO CInt|]),
+       ("c'guiSpinner", "GuiSpinner_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> CInt -> CInt -> CBool -> IO CInt|]),
+       ("c'guiValueBox", "GuiValueBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> CInt -> CInt -> CBool -> IO CInt|]),
+       ("c'guiValueBoxFloat", "GuiValueBoxFloat_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> Ptr CFloat -> CBool -> IO CInt|]),
+       ("c'guiTextBox", "GuiTextBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CInt -> CBool -> IO CInt|]),
+       ("c'guiSlider", "GuiSlider_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> Ptr CFloat -> CFloat -> CFloat -> IO CInt|]),
+       ("c'guiSliderBar", "GuiSliderBar_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> Ptr CFloat -> CFloat -> CFloat -> IO CInt|]),
+       ("c'guiProgressBar", "GuiProgressBar_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> Ptr CFloat -> CFloat -> CFloat -> IO CInt|]),
+       ("c'guiStatusBar", "GuiStatusBar_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
+       ("c'guiDummyRec", "GuiDummyRec_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
+       ("c'guiGrid", "GuiGrid_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CFloat -> CInt -> Ptr Vector2 -> IO CInt|]),
+       ("c'guiListView", "GuiListView_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CInt -> Ptr CInt -> IO CInt|]),
+       ("c'guiListViewEx", "GuiListViewEx_", "rgui_bindings.h", [t|Ptr Rectangle -> Ptr CString -> CInt -> Ptr CInt -> Ptr CInt -> Ptr CInt -> IO CInt|]),
+       ("c'guiMessageBox", "GuiMessageBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> CString -> IO CInt|]),
+       ("c'guiTextInputBox", "GuiTextInputBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> CString -> CString -> CString -> CInt -> Ptr CBool -> IO CInt|]),
+       ("c'guiColorPicker", "GuiColorPicker_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Color -> IO CInt|]),
+       ("c'guiColorPanel", "GuiColorPanel_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Color -> IO CInt|]),
+       ("c'guiColorBarAlpha", "GuiColorBarAlpha_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CFloat -> IO CInt|]),
+       ("c'guiColorBarHue", "GuiColorBarHue_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr CFloat -> IO CInt|]),
+       ("c'guiColorPickerHSV", "GuiColorPickerHSV_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Vector3 -> IO CInt|]),
+       ("c'guiColorPanelHSV", "GuiColorPanelHSV_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> Ptr Vector3 -> IO CInt|])
      ]
  )
 
diff --git a/src/Raylib/Util/GUI/Styles.hs b/src/Raylib/Util/GUI/Styles.hs
--- a/src/Raylib/Util/GUI/Styles.hs
+++ b/src/Raylib/Util/GUI/Styles.hs
@@ -22,18 +22,18 @@
 import Raylib.Internal.TH (genNative)
 
 $( genNative
-     [ ("c'guiLoadStyleAmber", "GuiLoadStyleAmber_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleAshes", "GuiLoadStyleAshes_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleBluish", "GuiLoadStyleBluish_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleCandy", "GuiLoadStyleCandy_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleCherry", "GuiLoadStyleCherry_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleCyber", "GuiLoadStyleCyber_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleDark", "GuiLoadStyleDark_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleEnefete", "GuiLoadStyleEnefete_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleJungle", "GuiLoadStyleJungle_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleLavanda", "GuiLoadStyleLavanda_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleSunny", "GuiLoadStyleSunny_", "rgui_bindings.h", [t|IO ()|], False),
-       ("c'guiLoadStyleTerminal", "GuiLoadStyleTerminal_", "rgui_bindings.h", [t|IO ()|], False)
+     [ ("c'guiLoadStyleAmber", "GuiLoadStyleAmber_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleAshes", "GuiLoadStyleAshes_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleBluish", "GuiLoadStyleBluish_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleCandy", "GuiLoadStyleCandy_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleCherry", "GuiLoadStyleCherry_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleCyber", "GuiLoadStyleCyber_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleDark", "GuiLoadStyleDark_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleEnefete", "GuiLoadStyleEnefete_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleJungle", "GuiLoadStyleJungle_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleLavanda", "GuiLoadStyleLavanda_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleSunny", "GuiLoadStyleSunny_", "rgui_bindings.h", [t|IO ()|]),
+       ("c'guiLoadStyleTerminal", "GuiLoadStyleTerminal_", "rgui_bindings.h", [t|IO ()|])
      ]
  )
 
diff --git a/src/Raylib/Util/RLGL.hs b/src/Raylib/Util/RLGL.hs
--- a/src/Raylib/Util/RLGL.hs
+++ b/src/Raylib/Util/RLGL.hs
@@ -169,6 +169,7 @@
     rlGetLocationAttrib,
     rlSetUniform,
     rlSetUniformMatrix,
+    rlSetUniformMatrices,
     rlSetUniformSampler,
     rlSetShader,
 
@@ -300,6 +301,7 @@
     c'rlGetLocationAttrib,
     c'rlSetUniform,
     c'rlSetUniformMatrix,
+    c'rlSetUniformMatrices,
     c'rlSetUniformSampler,
     c'rlSetShader,
     c'rlLoadComputeShaderProgram,
@@ -412,160 +414,161 @@
   )
 
 $( genNative
-     [ ("c'rlMatrixMode", "rlMatrixMode_", "rlgl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'rlTranslatef", "rlTranslatef_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|], False),
-       ("c'rlRotatef", "rlRotatef_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> CFloat -> IO ()|], False),
-       ("c'rlScalef", "rlScalef_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|], False),
-       ("c'rlMultMatrixf", "rlMultMatrixf_", "rlgl_bindings.h", [t|Ptr CFloat -> IO ()|], False),
-       ("c'rlFrustum", "rlFrustum_", "rlgl_bindings.h", [t|CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> IO ()|], False),
-       ("c'rlOrtho", "rlOrtho_", "rlgl_bindings.h", [t|CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> IO ()|], False),
-       ("c'rlViewport", "rlViewport_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'rlSetClipPlanes", "rlSetClipPlanes_", "rlgl_bindings.h", [t|CDouble -> CDouble -> IO ()|], False),
-       ("c'rlGetCullDistanceNear", "rlGetCullDistanceNear", "rlgl_bindings.h", [t|IO CDouble|], False),
-       ("c'rlGetCullDistanceFar", "rlGetCullDistanceFar", "rlgl_bindings.h", [t|IO CDouble|], False),
-       ("c'rlBegin", "rlBegin_", "rlgl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'rlVertex2i", "rlVertex2i_", "rlgl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'rlVertex2f", "rlVertex2f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> IO ()|], False),
-       ("c'rlVertex3f", "rlVertex3f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|], False),
-       ("c'rlTexCoord2f", "rlTexCoord2f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> IO ()|], False),
-       ("c'rlNormal3f", "rlNormal3f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|], False),
-       ("c'rlColor4ub", "rlColor4ub_", "rlgl_bindings.h", [t|CUChar -> CUChar -> CUChar -> CUChar -> IO ()|], False),
-       ("c'rlColor3f", "rlColor3f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|], False),
-       ("c'rlColor4f", "rlColor4f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> CFloat -> IO ()|], False),
-       ("c'rlEnableVertexArray", "rlEnableVertexArray_", "rlgl_bindings.h", [t|CUInt -> IO CBool|], False),
-       ("c'rlEnableVertexBuffer", "rlEnableVertexBuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlEnableVertexBufferElement", "rlEnableVertexBufferElement_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlEnableVertexAttribute", "rlEnableVertexAttribute_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlDisableVertexAttribute", "rlDisableVertexAttribute_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlActiveTextureSlot", "rlActiveTextureSlot_", "rlgl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'rlEnableTexture", "rlEnableTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlEnableTextureCubemap", "rlEnableTextureCubemap_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlTextureParameters", "rlTextureParameters_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> IO ()|], False),
-       ("c'rlCubemapParameters", "rlCubemapParameters_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> IO ()|], False),
-       ("c'rlEnableShader", "rlEnableShader_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlEnableFramebuffer", "rlEnableFramebuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlActiveDrawBuffers", "rlActiveDrawBuffers_", "rlgl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'rlBlitFramebuffer", "rlBlitFramebuffer_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'rlBindFramebuffer", "rlBindFramebuffer_", "rlgl_bindings.h", [t|CUInt -> CUInt -> IO ()|], False),
-       ("c'rlColorMask", "rlColorMask_", "rlgl_bindings.h", [t|CBool -> CBool -> CBool -> CBool -> IO ()|], False),
-       ("c'rlSetCullFace", "rlSetCullFace_", "rlgl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'rlScissor", "rlScissor_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'rlSetLineWidth", "rlSetLineWidth_", "rlgl_bindings.h", [t|CFloat -> IO ()|], False),
-       ("c'rlGetLineWidth", "rlGetLineWidth_", "rlgl_bindings.h", [t|IO CFloat|], False),
-       ("c'rlIsStereoRenderEnabled", "rlIsStereoRenderEnabled_", "rlgl_bindings.h", [t|IO CBool|], False),
-       ("c'rlClearColor", "rlClearColor_", "rlgl_bindings.h", [t|CUChar -> CUChar -> CUChar -> CUChar -> IO ()|], False),
-       ("c'rlSetBlendMode", "rlSetBlendMode_", "rlgl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'rlSetBlendFactors", "rlSetBlendFactors_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'rlSetBlendFactorsSeparate", "rlSetBlendFactorsSeparate_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'rlglInit", "rlglInit_", "rlgl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'rlLoadExtensions", "rlLoadExtensions_", "rlgl_bindings.h", [t|Ptr () -> IO ()|], False),
-       ("c'rlGetVersion", "rlGetVersion_", "rlgl_bindings.h", [t|IO CInt|], False),
-       ("c'rlSetFramebufferWidth", "rlSetFramebufferWidth_", "rlgl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'rlGetFramebufferWidth", "rlGetFramebufferWidth_", "rlgl_bindings.h", [t|IO CInt|], False),
-       ("c'rlSetFramebufferHeight", "rlSetFramebufferHeight_", "rlgl_bindings.h", [t|CInt -> IO ()|], False),
-       ("c'rlGetFramebufferHeight", "rlGetFramebufferHeight_", "rlgl_bindings.h", [t|IO CInt|], False),
-       ("c'rlGetTextureIdDefault", "rlGetTextureIdDefault_", "rlgl_bindings.h", [t|IO CUInt|], False),
-       ("c'rlGetShaderIdDefault", "rlGetShaderIdDefault_", "rlgl_bindings.h", [t|IO CUInt|], False),
-       ("c'rlGetShaderLocsDefault", "rlGetShaderLocsDefault_", "rlgl_bindings.h", [t|IO (Ptr CInt)|], False),
-       ("c'rlLoadRenderBatch", "rlLoadRenderBatch_", "rlgl_bindings.h", [t|CInt -> CInt -> IO (Ptr RLRenderBatch)|], False),
-       ("c'rlUnloadRenderBatch", "rlUnloadRenderBatch_", "rlgl_bindings.h", [t|Ptr RLRenderBatch -> IO ()|], False),
-       ("c'rlDrawRenderBatch", "rlDrawRenderBatch_", "rlgl_bindings.h", [t|Ptr RLRenderBatch -> IO ()|], False),
-       ("c'rlSetRenderBatchActive", "rlSetRenderBatchActive_", "rlgl_bindings.h", [t|Ptr RLRenderBatch -> IO ()|], False),
-       ("c'rlCheckRenderBatchLimit", "rlCheckRenderBatchLimit_", "rlgl_bindings.h", [t|CInt -> IO CBool|], False),
-       ("c'rlSetTexture", "rlSetTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlLoadVertexArray", "rlLoadVertexArray_", "rlgl_bindings.h", [t|IO CUInt|], False),
-       ("c'rlLoadVertexBuffer", "rlLoadVertexBuffer_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CBool -> IO CUInt|], False),
-       ("c'rlLoadVertexBufferElement", "rlLoadVertexBufferElement_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CBool -> IO CUInt|], False),
-       ("c'rlUpdateVertexBuffer", "rlUpdateVertexBuffer_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CInt -> CInt -> IO ()|], False),
-       ("c'rlUpdateVertexBufferElements", "rlUpdateVertexBufferElements_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CInt -> CInt -> IO ()|], False),
-       ("c'rlUnloadVertexArray", "rlUnloadVertexArray_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlUnloadVertexBuffer", "rlUnloadVertexBuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlSetVertexAttribute", "rlSetVertexAttribute_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> CBool -> CInt -> CInt -> IO ()|], False),
-       ("c'rlSetVertexAttributeDivisor", "rlSetVertexAttributeDivisor_", "rlgl_bindings.h", [t|CUInt -> CInt -> IO ()|], False),
-       ("c'rlSetVertexAttributeDefault", "rlSetVertexAttributeDefault_", "rlgl_bindings.h", [t|CInt -> Ptr () -> CInt -> CInt -> IO ()|], False),
-       ("c'rlDrawVertexArray", "rlDrawVertexArray_", "rlgl_bindings.h", [t|CInt -> CInt -> IO ()|], False),
-       ("c'rlDrawVertexArrayElements", "rlDrawVertexArrayElements_", "rlgl_bindings.h", [t|CInt -> CInt -> Ptr () -> IO ()|], False),
-       ("c'rlDrawVertexArrayInstanced", "rlDrawVertexArrayInstanced_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'rlDrawVertexArrayElementsInstanced", "rlDrawVertexArrayElementsInstanced_", "rlgl_bindings.h", [t|CInt -> CInt -> Ptr () -> CInt -> IO ()|], False),
-       ("c'rlLoadTexture", "rlLoadTexture_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CInt -> CInt -> CInt -> IO CUInt|], False),
-       ("c'rlLoadTextureDepth", "rlLoadTextureDepth_", "rlgl_bindings.h", [t|CInt -> CInt -> CBool -> IO CUInt|], False),
-       ("c'rlLoadTextureCubemap", "rlLoadTextureCubemap_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CInt -> IO CUInt|], False),
-       ("c'rlUpdateTexture", "rlUpdateTexture_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> CInt -> CInt -> CInt -> Ptr () -> IO ()|], False),
-       ("c'rlGetGlTextureFormats", "rlGetGlTextureFormats_", "rlgl_bindings.h", [t|CInt -> Ptr CUInt -> Ptr CUInt -> Ptr CUInt -> IO ()|], False),
-       ("c'rlGetPixelFormatName", "rlGetPixelFormatName_", "rlgl_bindings.h", [t|CUInt -> IO CString|], False),
-       ("c'rlUnloadTexture", "rlUnloadTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlGenTextureMipmaps", "rlGenTextureMipmaps_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> CInt -> Ptr CInt -> IO ()|], False),
-       ("c'rlReadTexturePixels", "rlReadTexturePixels_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> CInt -> IO (Ptr ())|], False),
-       ("c'rlReadScreenPixels", "rlReadScreenPixels_", "rlgl_bindings.h", [t|CInt -> CInt -> IO (Ptr CUChar)|], False),
-       ("c'rlLoadFramebuffer", "rlLoadFramebuffer_", "rlgl_bindings.h", [t|IO CUInt|], False),
-       ("c'rlFramebufferAttach", "rlFramebufferAttach_", "rlgl_bindings.h", [t|CUInt -> CUInt -> CInt -> CInt -> CInt -> IO ()|], False),
-       ("c'rlFramebufferComplete", "rlFramebufferComplete_", "rlgl_bindings.h", [t|CUInt -> IO CBool|], False),
-       ("c'rlUnloadFramebuffer", "rlUnloadFramebuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlLoadShaderCode", "rlLoadShaderCode_", "rlgl_bindings.h", [t|CString -> CString -> IO CUInt|], False),
-       ("c'rlCompileShader", "rlCompileShader_", "rlgl_bindings.h", [t|CString -> CInt -> IO CUInt|], False),
-       ("c'rlLoadShaderProgram", "rlLoadShaderProgram_", "rlgl_bindings.h", [t|CUInt -> CUInt -> IO CUInt|], False),
-       ("c'rlUnloadShaderProgram", "rlUnloadShaderProgram_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlGetLocationUniform", "rlGetLocationUniform_", "rlgl_bindings.h", [t|CUInt -> CString -> IO CInt|], False),
-       ("c'rlGetLocationAttrib", "rlGetLocationAttrib_", "rlgl_bindings.h", [t|CUInt -> CString -> IO CInt|], False),
-       ("c'rlSetUniform", "rlSetUniform_", "rlgl_bindings.h", [t|CInt -> Ptr () -> CInt -> CInt -> IO ()|], False),
-       ("c'rlSetUniformMatrix", "rlSetUniformMatrix_", "rlgl_bindings.h", [t|CInt -> Ptr Matrix -> IO ()|], False),
-       ("c'rlSetUniformSampler", "rlSetUniformSampler_", "rlgl_bindings.h", [t|CInt -> CUInt -> IO ()|], False),
-       ("c'rlSetShader", "rlSetShader_", "rlgl_bindings.h", [t|CUInt -> Ptr CInt -> IO ()|], False),
-       ("c'rlLoadComputeShaderProgram", "rlLoadComputeShaderProgram_", "rlgl_bindings.h", [t|CUInt -> IO CUInt|], False),
-       ("c'rlComputeShaderDispatch", "rlComputeShaderDispatch_", "rlgl_bindings.h", [t|CUInt -> CUInt -> CUInt -> IO ()|], False),
-       ("c'rlLoadShaderBuffer", "rlLoadShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CInt -> IO CUInt|], False),
-       ("c'rlUnloadShaderBuffer", "rlUnloadShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|], False),
-       ("c'rlUpdateShaderBuffer", "rlUpdateShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CUInt -> CUInt -> IO ()|], False),
-       ("c'rlBindShaderBuffer", "rlBindShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> CUInt -> IO ()|], False),
-       ("c'rlReadShaderBuffer", "rlReadShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CUInt -> CUInt -> IO ()|], False),
-       ("c'rlCopyShaderBuffer", "rlCopyShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> CUInt -> CUInt -> CUInt -> CUInt -> IO ()|], False),
-       ("c'rlGetShaderBufferSize", "rlGetShaderBufferSize_", "rlgl_bindings.h", [t|CUInt -> IO CUInt|], False),
-       ("c'rlBindImageTexture", "rlBindImageTexture_", "rlgl_bindings.h", [t|CUInt -> CUInt -> CInt -> CBool -> IO ()|], False),
-       ("c'rlGetMatrixModelview", "rlGetMatrixModelview_", "rlgl_bindings.h", [t|IO (Ptr Matrix)|], False),
-       ("c'rlGetMatrixProjection", "rlGetMatrixProjection_", "rlgl_bindings.h", [t|IO (Ptr Matrix)|], False),
-       ("c'rlGetMatrixTransform", "rlGetMatrixTransform_", "rlgl_bindings.h", [t|IO (Ptr Matrix)|], False),
-       ("c'rlGetMatrixProjectionStereo", "rlGetMatrixProjectionStereo_", "rlgl_bindings.h", [t|CInt -> IO (Ptr Matrix)|], False),
-       ("c'rlGetMatrixViewOffsetStereo", "rlGetMatrixViewOffsetStereo_", "rlgl_bindings.h", [t|CInt -> IO (Ptr Matrix)|], False),
-       ("c'rlSetMatrixProjection", "rlSetMatrixProjection_", "rlgl_bindings.h", [t|Ptr Matrix -> IO ()|], False),
-       ("c'rlSetMatrixModelview", "rlSetMatrixModelview_", "rlgl_bindings.h", [t|Ptr Matrix -> IO ()|], False),
-       ("c'rlSetMatrixProjectionStereo", "rlSetMatrixProjectionStereo_", "rlgl_bindings.h", [t|Ptr Matrix -> Ptr Matrix -> IO ()|], False),
-       ("c'rlSetMatrixViewOffsetStereo", "rlSetMatrixViewOffsetStereo_", "rlgl_bindings.h", [t|Ptr Matrix -> Ptr Matrix -> IO ()|], False),
-       ("c'rlGetPixelDataSize", "rlGetPixelDataSize", "rl_internal.h", [t|CInt -> CInt -> CInt -> IO CInt|], False),
-       ("c'rlPushMatrix", "rlPushMatrix_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlPopMatrix", "rlPopMatrix_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlLoadIdentity", "rlLoadIdentity_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlEnd", "rlEnd_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableVertexArray", "rlDisableVertexArray_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableVertexBuffer", "rlDisableVertexBuffer_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableVertexBufferElement", "rlDisableVertexBufferElement_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableTexture", "rlDisableTexture_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableTextureCubemap", "rlDisableTextureCubemap_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableShader", "rlDisableShader_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableFramebuffer", "rlDisableFramebuffer_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlGetActiveFramebuffer", "rlGetActiveFramebuffer_", "rlgl_bindings.h", [t|IO CUInt|], False),
-       ("c'rlEnableColorBlend", "rlEnableColorBlend_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableColorBlend", "rlDisableColorBlend_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlEnableDepthTest", "rlEnableDepthTest_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableDepthTest", "rlDisableDepthTest_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlEnableDepthMask", "rlEnableDepthMask_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableDepthMask", "rlDisableDepthMask_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlEnableBackfaceCulling", "rlEnableBackfaceCulling_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableBackfaceCulling", "rlDisableBackfaceCulling_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlEnableScissorTest", "rlEnableScissorTest_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableScissorTest", "rlDisableScissorTest_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlEnableWireMode", "rlEnableWireMode_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlEnablePointMode", "rlEnablePointMode_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableWireMode", "rlDisableWireMode_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlEnableSmoothLines", "rlEnableSmoothLines_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableSmoothLines", "rlDisableSmoothLines_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlEnableStereoRender", "rlEnableStereoRender_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDisableStereoRender", "rlDisableStereoRender_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlClearScreenBuffers", "rlClearScreenBuffers_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlCheckErrors", "rlCheckErrors_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlglClose", "rlglClose_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlDrawRenderBatchActive", "rlDrawRenderBatchActive_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlLoadDrawCube", "rlLoadDrawCube_", "rlgl_bindings.h", [t|IO ()|], False),
-       ("c'rlLoadDrawQuad", "rlLoadDrawQuad_", "rlgl_bindings.h", [t|IO ()|], False)
+     [ ("c'rlMatrixMode", "rlMatrixMode_", "rlgl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'rlTranslatef", "rlTranslatef_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|]),
+       ("c'rlRotatef", "rlRotatef_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> CFloat -> IO ()|]),
+       ("c'rlScalef", "rlScalef_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|]),
+       ("c'rlMultMatrixf", "rlMultMatrixf_", "rlgl_bindings.h", [t|Ptr CFloat -> IO ()|]),
+       ("c'rlFrustum", "rlFrustum_", "rlgl_bindings.h", [t|CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> IO ()|]),
+       ("c'rlOrtho", "rlOrtho_", "rlgl_bindings.h", [t|CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> CDouble -> IO ()|]),
+       ("c'rlViewport", "rlViewport_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> IO ()|]),
+       ("c'rlSetClipPlanes", "rlSetClipPlanes_", "rlgl_bindings.h", [t|CDouble -> CDouble -> IO ()|]),
+       ("c'rlGetCullDistanceNear", "rlGetCullDistanceNear", "rlgl_bindings.h", [t|IO CDouble|]),
+       ("c'rlGetCullDistanceFar", "rlGetCullDistanceFar", "rlgl_bindings.h", [t|IO CDouble|]),
+       ("c'rlBegin", "rlBegin_", "rlgl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'rlVertex2i", "rlVertex2i_", "rlgl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'rlVertex2f", "rlVertex2f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> IO ()|]),
+       ("c'rlVertex3f", "rlVertex3f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|]),
+       ("c'rlTexCoord2f", "rlTexCoord2f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> IO ()|]),
+       ("c'rlNormal3f", "rlNormal3f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|]),
+       ("c'rlColor4ub", "rlColor4ub_", "rlgl_bindings.h", [t|CUChar -> CUChar -> CUChar -> CUChar -> IO ()|]),
+       ("c'rlColor3f", "rlColor3f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> IO ()|]),
+       ("c'rlColor4f", "rlColor4f_", "rlgl_bindings.h", [t|CFloat -> CFloat -> CFloat -> CFloat -> IO ()|]),
+       ("c'rlEnableVertexArray", "rlEnableVertexArray_", "rlgl_bindings.h", [t|CUInt -> IO CBool|]),
+       ("c'rlEnableVertexBuffer", "rlEnableVertexBuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlEnableVertexBufferElement", "rlEnableVertexBufferElement_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlEnableVertexAttribute", "rlEnableVertexAttribute_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlDisableVertexAttribute", "rlDisableVertexAttribute_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlActiveTextureSlot", "rlActiveTextureSlot_", "rlgl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'rlEnableTexture", "rlEnableTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlEnableTextureCubemap", "rlEnableTextureCubemap_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlTextureParameters", "rlTextureParameters_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> IO ()|]),
+       ("c'rlCubemapParameters", "rlCubemapParameters_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> IO ()|]),
+       ("c'rlEnableShader", "rlEnableShader_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlEnableFramebuffer", "rlEnableFramebuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlActiveDrawBuffers", "rlActiveDrawBuffers_", "rlgl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'rlBlitFramebuffer", "rlBlitFramebuffer_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> IO ()|]),
+       ("c'rlBindFramebuffer", "rlBindFramebuffer_", "rlgl_bindings.h", [t|CUInt -> CUInt -> IO ()|]),
+       ("c'rlColorMask", "rlColorMask_", "rlgl_bindings.h", [t|CBool -> CBool -> CBool -> CBool -> IO ()|]),
+       ("c'rlSetCullFace", "rlSetCullFace_", "rlgl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'rlScissor", "rlScissor_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> IO ()|]),
+       ("c'rlSetLineWidth", "rlSetLineWidth_", "rlgl_bindings.h", [t|CFloat -> IO ()|]),
+       ("c'rlGetLineWidth", "rlGetLineWidth_", "rlgl_bindings.h", [t|IO CFloat|]),
+       ("c'rlIsStereoRenderEnabled", "rlIsStereoRenderEnabled_", "rlgl_bindings.h", [t|IO CBool|]),
+       ("c'rlClearColor", "rlClearColor_", "rlgl_bindings.h", [t|CUChar -> CUChar -> CUChar -> CUChar -> IO ()|]),
+       ("c'rlSetBlendMode", "rlSetBlendMode_", "rlgl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'rlSetBlendFactors", "rlSetBlendFactors_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> IO ()|]),
+       ("c'rlSetBlendFactorsSeparate", "rlSetBlendFactorsSeparate_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> IO ()|]),
+       ("c'rlglInit", "rlglInit_", "rlgl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'rlLoadExtensions", "rlLoadExtensions_", "rlgl_bindings.h", [t|Ptr () -> IO ()|]),
+       ("c'rlGetVersion", "rlGetVersion_", "rlgl_bindings.h", [t|IO CInt|]),
+       ("c'rlSetFramebufferWidth", "rlSetFramebufferWidth_", "rlgl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'rlGetFramebufferWidth", "rlGetFramebufferWidth_", "rlgl_bindings.h", [t|IO CInt|]),
+       ("c'rlSetFramebufferHeight", "rlSetFramebufferHeight_", "rlgl_bindings.h", [t|CInt -> IO ()|]),
+       ("c'rlGetFramebufferHeight", "rlGetFramebufferHeight_", "rlgl_bindings.h", [t|IO CInt|]),
+       ("c'rlGetTextureIdDefault", "rlGetTextureIdDefault_", "rlgl_bindings.h", [t|IO CUInt|]),
+       ("c'rlGetShaderIdDefault", "rlGetShaderIdDefault_", "rlgl_bindings.h", [t|IO CUInt|]),
+       ("c'rlGetShaderLocsDefault", "rlGetShaderLocsDefault_", "rlgl_bindings.h", [t|IO (Ptr CInt)|]),
+       ("c'rlLoadRenderBatch", "rlLoadRenderBatch_", "rlgl_bindings.h", [t|CInt -> CInt -> IO (Ptr RLRenderBatch)|]),
+       ("c'rlUnloadRenderBatch", "rlUnloadRenderBatch_", "rlgl_bindings.h", [t|Ptr RLRenderBatch -> IO ()|]),
+       ("c'rlDrawRenderBatch", "rlDrawRenderBatch_", "rlgl_bindings.h", [t|Ptr RLRenderBatch -> IO ()|]),
+       ("c'rlSetRenderBatchActive", "rlSetRenderBatchActive_", "rlgl_bindings.h", [t|Ptr RLRenderBatch -> IO ()|]),
+       ("c'rlCheckRenderBatchLimit", "rlCheckRenderBatchLimit_", "rlgl_bindings.h", [t|CInt -> IO CBool|]),
+       ("c'rlSetTexture", "rlSetTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlLoadVertexArray", "rlLoadVertexArray_", "rlgl_bindings.h", [t|IO CUInt|]),
+       ("c'rlLoadVertexBuffer", "rlLoadVertexBuffer_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CBool -> IO CUInt|]),
+       ("c'rlLoadVertexBufferElement", "rlLoadVertexBufferElement_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CBool -> IO CUInt|]),
+       ("c'rlUpdateVertexBuffer", "rlUpdateVertexBuffer_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CInt -> CInt -> IO ()|]),
+       ("c'rlUpdateVertexBufferElements", "rlUpdateVertexBufferElements_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CInt -> CInt -> IO ()|]),
+       ("c'rlUnloadVertexArray", "rlUnloadVertexArray_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlUnloadVertexBuffer", "rlUnloadVertexBuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlSetVertexAttribute", "rlSetVertexAttribute_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> CBool -> CInt -> CInt -> IO ()|]),
+       ("c'rlSetVertexAttributeDivisor", "rlSetVertexAttributeDivisor_", "rlgl_bindings.h", [t|CUInt -> CInt -> IO ()|]),
+       ("c'rlSetVertexAttributeDefault", "rlSetVertexAttributeDefault_", "rlgl_bindings.h", [t|CInt -> Ptr () -> CInt -> CInt -> IO ()|]),
+       ("c'rlDrawVertexArray", "rlDrawVertexArray_", "rlgl_bindings.h", [t|CInt -> CInt -> IO ()|]),
+       ("c'rlDrawVertexArrayElements", "rlDrawVertexArrayElements_", "rlgl_bindings.h", [t|CInt -> CInt -> Ptr () -> IO ()|]),
+       ("c'rlDrawVertexArrayInstanced", "rlDrawVertexArrayInstanced_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> IO ()|]),
+       ("c'rlDrawVertexArrayElementsInstanced", "rlDrawVertexArrayElementsInstanced_", "rlgl_bindings.h", [t|CInt -> CInt -> Ptr () -> CInt -> IO ()|]),
+       ("c'rlLoadTexture", "rlLoadTexture_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CInt -> CInt -> CInt -> IO CUInt|]),
+       ("c'rlLoadTextureDepth", "rlLoadTextureDepth_", "rlgl_bindings.h", [t|CInt -> CInt -> CBool -> IO CUInt|]),
+       ("c'rlLoadTextureCubemap", "rlLoadTextureCubemap_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CInt -> IO CUInt|]),
+       ("c'rlUpdateTexture", "rlUpdateTexture_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> CInt -> CInt -> CInt -> Ptr () -> IO ()|]),
+       ("c'rlGetGlTextureFormats", "rlGetGlTextureFormats_", "rlgl_bindings.h", [t|CInt -> Ptr CUInt -> Ptr CUInt -> Ptr CUInt -> IO ()|]),
+       ("c'rlGetPixelFormatName", "rlGetPixelFormatName_", "rlgl_bindings.h", [t|CUInt -> IO CString|]),
+       ("c'rlUnloadTexture", "rlUnloadTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlGenTextureMipmaps", "rlGenTextureMipmaps_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> CInt -> Ptr CInt -> IO ()|]),
+       ("c'rlReadTexturePixels", "rlReadTexturePixels_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> CInt -> IO (Ptr ())|]),
+       ("c'rlReadScreenPixels", "rlReadScreenPixels_", "rlgl_bindings.h", [t|CInt -> CInt -> IO (Ptr CUChar)|]),
+       ("c'rlLoadFramebuffer", "rlLoadFramebuffer_", "rlgl_bindings.h", [t|IO CUInt|]),
+       ("c'rlFramebufferAttach", "rlFramebufferAttach_", "rlgl_bindings.h", [t|CUInt -> CUInt -> CInt -> CInt -> CInt -> IO ()|]),
+       ("c'rlFramebufferComplete", "rlFramebufferComplete_", "rlgl_bindings.h", [t|CUInt -> IO CBool|]),
+       ("c'rlUnloadFramebuffer", "rlUnloadFramebuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlLoadShaderCode", "rlLoadShaderCode_", "rlgl_bindings.h", [t|CString -> CString -> IO CUInt|]),
+       ("c'rlCompileShader", "rlCompileShader_", "rlgl_bindings.h", [t|CString -> CInt -> IO CUInt|]),
+       ("c'rlLoadShaderProgram", "rlLoadShaderProgram_", "rlgl_bindings.h", [t|CUInt -> CUInt -> IO CUInt|]),
+       ("c'rlUnloadShaderProgram", "rlUnloadShaderProgram_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlGetLocationUniform", "rlGetLocationUniform_", "rlgl_bindings.h", [t|CUInt -> CString -> IO CInt|]),
+       ("c'rlGetLocationAttrib", "rlGetLocationAttrib_", "rlgl_bindings.h", [t|CUInt -> CString -> IO CInt|]),
+       ("c'rlSetUniform", "rlSetUniform_", "rlgl_bindings.h", [t|CInt -> Ptr () -> CInt -> CInt -> IO ()|]),
+       ("c'rlSetUniformMatrix", "rlSetUniformMatrix_", "rlgl_bindings.h", [t|CInt -> Ptr Matrix -> IO ()|]),
+       ("c'rlSetUniformMatrices", "rlSetUniformMatrices_", "rlgl_bindings.h", [t|CInt -> Ptr Matrix -> CInt -> IO ()|]),
+       ("c'rlSetUniformSampler", "rlSetUniformSampler_", "rlgl_bindings.h", [t|CInt -> CUInt -> IO ()|]),
+       ("c'rlSetShader", "rlSetShader_", "rlgl_bindings.h", [t|CUInt -> Ptr CInt -> IO ()|]),
+       ("c'rlLoadComputeShaderProgram", "rlLoadComputeShaderProgram_", "rlgl_bindings.h", [t|CUInt -> IO CUInt|]),
+       ("c'rlComputeShaderDispatch", "rlComputeShaderDispatch_", "rlgl_bindings.h", [t|CUInt -> CUInt -> CUInt -> IO ()|]),
+       ("c'rlLoadShaderBuffer", "rlLoadShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CInt -> IO CUInt|]),
+       ("c'rlUnloadShaderBuffer", "rlUnloadShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
+       ("c'rlUpdateShaderBuffer", "rlUpdateShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CUInt -> CUInt -> IO ()|]),
+       ("c'rlBindShaderBuffer", "rlBindShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> CUInt -> IO ()|]),
+       ("c'rlReadShaderBuffer", "rlReadShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> Ptr () -> CUInt -> CUInt -> IO ()|]),
+       ("c'rlCopyShaderBuffer", "rlCopyShaderBuffer_", "rlgl_bindings.h", [t|CUInt -> CUInt -> CUInt -> CUInt -> CUInt -> IO ()|]),
+       ("c'rlGetShaderBufferSize", "rlGetShaderBufferSize_", "rlgl_bindings.h", [t|CUInt -> IO CUInt|]),
+       ("c'rlBindImageTexture", "rlBindImageTexture_", "rlgl_bindings.h", [t|CUInt -> CUInt -> CInt -> CBool -> IO ()|]),
+       ("c'rlGetMatrixModelview", "rlGetMatrixModelview_", "rlgl_bindings.h", [t|IO (Ptr Matrix)|]),
+       ("c'rlGetMatrixProjection", "rlGetMatrixProjection_", "rlgl_bindings.h", [t|IO (Ptr Matrix)|]),
+       ("c'rlGetMatrixTransform", "rlGetMatrixTransform_", "rlgl_bindings.h", [t|IO (Ptr Matrix)|]),
+       ("c'rlGetMatrixProjectionStereo", "rlGetMatrixProjectionStereo_", "rlgl_bindings.h", [t|CInt -> IO (Ptr Matrix)|]),
+       ("c'rlGetMatrixViewOffsetStereo", "rlGetMatrixViewOffsetStereo_", "rlgl_bindings.h", [t|CInt -> IO (Ptr Matrix)|]),
+       ("c'rlSetMatrixProjection", "rlSetMatrixProjection_", "rlgl_bindings.h", [t|Ptr Matrix -> IO ()|]),
+       ("c'rlSetMatrixModelview", "rlSetMatrixModelview_", "rlgl_bindings.h", [t|Ptr Matrix -> IO ()|]),
+       ("c'rlSetMatrixProjectionStereo", "rlSetMatrixProjectionStereo_", "rlgl_bindings.h", [t|Ptr Matrix -> Ptr Matrix -> IO ()|]),
+       ("c'rlSetMatrixViewOffsetStereo", "rlSetMatrixViewOffsetStereo_", "rlgl_bindings.h", [t|Ptr Matrix -> Ptr Matrix -> IO ()|]),
+       ("c'rlGetPixelDataSize", "rlGetPixelDataSize", "rl_internal.h", [t|CInt -> CInt -> CInt -> IO CInt|]),
+       ("c'rlPushMatrix", "rlPushMatrix_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlPopMatrix", "rlPopMatrix_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlLoadIdentity", "rlLoadIdentity_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlEnd", "rlEnd_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableVertexArray", "rlDisableVertexArray_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableVertexBuffer", "rlDisableVertexBuffer_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableVertexBufferElement", "rlDisableVertexBufferElement_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableTexture", "rlDisableTexture_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableTextureCubemap", "rlDisableTextureCubemap_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableShader", "rlDisableShader_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableFramebuffer", "rlDisableFramebuffer_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlGetActiveFramebuffer", "rlGetActiveFramebuffer_", "rlgl_bindings.h", [t|IO CUInt|]),
+       ("c'rlEnableColorBlend", "rlEnableColorBlend_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableColorBlend", "rlDisableColorBlend_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlEnableDepthTest", "rlEnableDepthTest_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableDepthTest", "rlDisableDepthTest_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlEnableDepthMask", "rlEnableDepthMask_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableDepthMask", "rlDisableDepthMask_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlEnableBackfaceCulling", "rlEnableBackfaceCulling_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableBackfaceCulling", "rlDisableBackfaceCulling_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlEnableScissorTest", "rlEnableScissorTest_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableScissorTest", "rlDisableScissorTest_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlEnableWireMode", "rlEnableWireMode_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlEnablePointMode", "rlEnablePointMode_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableWireMode", "rlDisableWireMode_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlEnableSmoothLines", "rlEnableSmoothLines_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableSmoothLines", "rlDisableSmoothLines_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlEnableStereoRender", "rlEnableStereoRender_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisableStereoRender", "rlDisableStereoRender_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlClearScreenBuffers", "rlClearScreenBuffers_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlCheckErrors", "rlCheckErrors_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlglClose", "rlglClose_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDrawRenderBatchActive", "rlDrawRenderBatchActive_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlLoadDrawCube", "rlLoadDrawCube_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlLoadDrawQuad", "rlLoadDrawQuad_", "rlgl_bindings.h", [t|IO ()|])
      ]
  )
 
@@ -1191,6 +1194,10 @@
 -- | Set shader value matrix
 rlSetUniformMatrix :: Int -> Matrix -> IO ()
 rlSetUniformMatrix locIndex mat = withFreeable mat (c'rlSetUniformMatrix (fromIntegral locIndex))
+
+-- | Set shader value matrices
+rlSetUniformMatrices :: Int -> [Matrix] -> IO ()
+rlSetUniformMatrices locIndex mats = withFreeableArrayLen mats (\c m -> c'rlSetUniformMatrices (fromIntegral locIndex) m (fromIntegral c))
 
 -- | Set shader value sampler
 rlSetUniformSampler :: Int -> Integer -> IO ()
