瀏覽代碼

Merge branch 'master' into sdf

Conflicts:
	stb_truetype.h
Sean Barrett 8 年之前
父節點
當前提交
fa98e4f6cf
共有 54 個文件被更改,包括 8750 次插入1165 次删除
  1. 28 0
      .github/CONTRIBUTING.md
  2. 6 0
      .github/PULL_REQUEST_TEMPLATE.md
  3. 5 0
      .travis.yml
  4. 64 33
      README.md
  5. 二進制
      data/map_01.png
  6. 二進制
      data/map_02.png
  7. 二進制
      data/map_03.png
  8. 1055 0
      deprecated/rrsprintf.h
  9. 1 0
      docs/other_libs.md
  10. 3 3
      docs/stb_howto.txt
  11. 3 3
      docs/why_public_domain.md
  12. 313 75
      stb.h
  13. 201 48
      stb_c_lexer.h
  14. 1045 0
      stb_connected_components.h
  15. 46 0
      stb_divide.h
  16. 72 9
      stb_dxt.h
  17. 93 10
      stb_easy_font.h
  18. 5 2
      stb_herringbone_wang_tile.h
  19. 402 250
      stb_image.h
  20. 170 131
      stb_image_resize.h
  21. 497 135
      stb_image_write.h
  22. 52 4
      stb_leakcheck.h
  23. 153 12
      stb_perlin.h
  24. 95 20
      stb_rect_pack.h
  25. 1202 0
      stb_sprintf.h
  26. 165 62
      stb_textedit.h
  27. 59 7
      stb_tilemap_editor.h
  28. 743 54
      stb_truetype.h
  29. 210 247
      stb_vorbis.c
  30. 78 13
      stb_voxel_render.h
  31. 50 3
      stretchy_buffer.h
  32. 7 0
      tests/Makefile
  33. 47 0
      tests/c_lexer_test.c
  34. 1 1
      tests/caveview/cave_render.c
  35. 363 0
      tests/grid_reachability.c
  36. 19 2
      tests/image_test.c
  37. 2 0
      tests/oversample/stb_wingraph.h
  38. 124 0
      tests/pg_test/pg_test.c
  39. 155 20
      tests/resample_test.cpp
  40. 3 0
      tests/resample_test_c.c
  41. 26 2
      tests/stb.dsp
  42. 37 1
      tests/stb.dsw
  43. 14 0
      tests/test_c_compilation.c
  44. 9 0
      tests/test_cpp_compilation.cpp
  45. 14 2
      tests/test_truetype.c
  46. 6 1
      tests/test_vorbis.c
  47. 125 0
      tests/vorbseek/vorbseek.c
  48. 96 0
      tests/vorbseek/vorbseek.dsp
  49. 36 11
      tools/README.footer.md
  50. 5 1
      tools/README.header.md
  51. 3 1
      tools/README.list
  52. 5 2
      tools/make_readme.c
  53. 749 0
      tools/unicode.c
  54. 88 0
      tools/unicode/unicode.dsp

+ 28 - 0
.github/CONTRIBUTING.md

@@ -0,0 +1,28 @@
+Pull Requests and Issues are both welcome.
+
+# Responsiveness
+
+General priority order is:
+
+* Crashes
+* Bugs
+* Warnings
+* Enhancements (new features, performance improvement, etc)
+
+Pull requests get priority over Issues. Some pull requests I take
+as written; some I modify myself; some I will request changes before
+accepting them. Because I've ended up supporting a lot of libraries
+(20 as I write this, with more on the way), I am somewhat slow to
+address things. Many issues have been around for a long time.
+
+# Pull requests
+
+* Do NOT update the version number in the file. (This just causes conflicts.)
+* Do add your name to the list of contributors. (Don't worry about the formatting.) I'll try to remember to add it if you don't, but I sometimes forget as it's an extra step.
+
+# Specific libraries
+
+I generally do not want new file formats for stb_image because
+we are trying to improve its security, so increasing its attack
+surface is counter-productive.
+

+ 6 - 0
.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,6 @@
+* Delete this list before clicking CREATE PULL REQUEST
+* Make sure you're using a special branch just for this pull request. (Sometimes people unknowingly use a default branch, then later update that branch, which updates the pull request with the other changes if it hasn't been merged yet.)
+* Do NOT update the version number in the file. (This just causes conflicts.)
+* Do add your name to the list of contributors. (Don't worry about the formatting.) I'll try to remember to add it if you don't, but I sometimes forget as it's an extra step.
+
+If you get something above wrong, don't fret it, it's not the end of the world.

+ 5 - 0
.travis.yml

@@ -0,0 +1,5 @@
+language: C
+install: true
+script:
+  - cd tests
+  - make all

+ 64 - 33
README.md

@@ -3,31 +3,37 @@
 stb
 ===
 
-single-file public domain libraries for C/C++
+single-file public domain (or MIT licensed) libraries for C/C++ <a name="stb_libs"></a>
+
+Most libraries by stb, except: stb_dxt by Fabian "ryg" Giesen, stb_image_resize
+by Jorge L. "VinoBS" Rodriguez, and stb_sprintf by Jeff Roberts.
+
 
 library    | lastest version | category | LoC | description
 --------------------- | ---- | -------- | --- | --------------------------------
-**stb_vorbis.c** | 1.05 | audio | 5445 | decode ogg vorbis files from file/memory to float/16-bit signed output
-**stb_image.h** | 2.06 | graphics | 6437 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
-**stb_truetype.h** | 1.06 | graphics | 2632 | parse, decode, and rasterize characters from truetype fonts
-**stb_image_write.h** | 0.98 | graphics | 730 | image writing to disk: PNG, TGA, BMP
-**stb_image_resize.h** | 0.90 | graphics | 2585 | resize images larger/smaller with good quality
-**stb_rect_pack.h** | 0.06 | graphics | 560 | simple 2D rectangle packer with decent quality
-**stretchy_buffer.h** | 1.02 | utility | 210 | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
-**stb_textedit.h** | 1.6 | UI | 1290 | guts of a text editor for games etc implementing them from scratch
-**stb_voxel_render.h** | 0.81 | 3D&nbsp;graphics | 3644 | Minecraft-esque voxel rendering "engine" with many more features
-**stb_dxt.h** | 1.04 | 3D&nbsp;graphics | 624 | Fabian "ryg" Giesen's real-time DXT compressor
-**stb_perlin.h** | 0.2 | 3D&nbsp;graphics | 175 | revised Perlin noise (3D input, 1D output)
-**stb_easy_font.h** | 0.5 | 3D&nbsp;graphics | 220 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
-**stb_tilemap_editor.h** | 0.35 | game&nbsp;dev | 4120 | embeddable tilemap editor
-**stb_herringbone_wa...** | 0.6 | game&nbsp;dev | 1217 | herringbone Wang tile map generator
-**stb_c_lexer.h** | 0.06 | parsing | 809 | simplify writing parsers for C-like languages
-**stb_divide.h** | 0.91 | math | 373 | more useful 32-bit modulus e.g. "euclidean divide"
-**stb.h** | 2.24 | misc | 14086 | helper functions for C, mostly redundant in C++; basically author's personal stuff
-**stb_leakcheck.h** | 0.2 | misc | 117 | quick-and-dirty malloc/free leak-checking
-
-Total libraries: 18  
-Total lines of C code: 45274
+**[stb_vorbis.c](stb_vorbis.c)** | 1.10 | audio | 5447 | decode ogg vorbis files from file/memory to float/16-bit signed output
+**[stb_image.h](stb_image.h)** | 2.15 | graphics | 7177 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
+**[stb_truetype.h](stb_truetype.h)** | 1.15 | graphics | 4061 | parse, decode, and rasterize characters from truetype fonts
+**[stb_image_write.h](stb_image_write.h)** | 1.05 | graphics | 1092 | image writing to disk: PNG, TGA, BMP
+**[stb_image_resize.h](stb_image_resize.h)** | 0.94 | graphics | 2624 | resize images larger/smaller with good quality
+**[stb_rect_pack.h](stb_rect_pack.h)** | 0.11 | graphics | 635 | simple 2D rectangle packer with decent quality
+**[stb_sprintf.h](stb_sprintf.h)** | 1.02 | utility | 1202 | fast sprintf, snprintf for C/C++
+**[stretchy_buffer.h](stretchy_buffer.h)** | 1.02 | utility | 257 | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
+**[stb_textedit.h](stb_textedit.h)** | 1.11 | user&nbsp;interface | 1393 | guts of a text editor for games etc implementing them from scratch
+**[stb_voxel_render.h](stb_voxel_render.h)** | 0.85 | 3D&nbsp;graphics | 3803 | Minecraft-esque voxel rendering "engine" with many more features
+**[stb_dxt.h](stb_dxt.h)** | 1.06 | 3D&nbsp;graphics | 687 | Fabian "ryg" Giesen's real-time DXT compressor
+**[stb_perlin.h](stb_perlin.h)** | 0.3 | 3D&nbsp;graphics | 316 | revised Perlin noise (3D input, 1D output)
+**[stb_easy_font.h](stb_easy_font.h)** | 1.0 | 3D&nbsp;graphics | 303 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
+**[stb_tilemap_editor.h](stb_tilemap_editor.h)** | 0.38 | game&nbsp;dev | 4172 | embeddable tilemap editor
+**[stb_herringbone_wa...](stb_herringbone_wang_tile.h)** | 0.6 | game&nbsp;dev | 1220 | herringbone Wang tile map generator
+**[stb_c_lexer.h](stb_c_lexer.h)** | 0.09 | parsing | 962 | simplify writing parsers for C-like languages
+**[stb_divide.h](stb_divide.h)** | 0.91 | math | 419 | more useful 32-bit modulus e.g. "euclidean divide"
+**[stb_connected_comp...](stb_connected_components.h)** | 0.95 | misc | 1045 | incrementally compute reachability on grids
+**[stb.h](stb.h)** | 2.29 | misc | 14324 | helper functions for C, mostly redundant in C++; basically author's personal stuff
+**[stb_leakcheck.h](stb_leakcheck.h)** | 0.3 | misc | 165 | quick-and-dirty malloc/free leak-checking
+
+Total libraries: 20  
+Total lines of C code: 51304
 
 
 FAQ
@@ -35,15 +41,40 @@ FAQ
 
 #### What's the license?
 
-These libraries are in the public domain (or the equivalent where that is not
-possible). You can do anything you want with them. You have no legal obligation
+These libraries are in the public domain. You can do anything you
+want with them. You have no legal obligation
 to do anything else, although I appreciate attribution.
 
-#### If I wrap an stb library in a new library, does the new library have to be public domain?
+They are also licensed under the MIT open source license, if you have lawyers
+who are unhappy with public domain. Every source file includes an explicit
+dual-license for you to choose from.
+
+#### <a name="other_libs"></a> Are there other single-file public-domain/open source libraries with minimal dependencies out there?
+
+[Yes.](https://github.com/nothings/single_file_libs)
+
+#### If I wrap an stb library in a new library, does the new library have to be public domain/MIT?
+
+No, because it's public domain you can freely relicense it to whatever license your new
+library wants to be.
 
-No.
+#### What's the deal with SSE support in GCC-based compilers?
 
-#### A lot of these libraries seem redundant to existing open source libraries. Are they better somehow?
+stb_image will either use SSE2 (if you compile with -msse2) or
+will not use any SIMD at all, rather than trying to detect the
+processor at runtime and handle it correctly. As I understand it,
+the approved path in GCC for runtime-detection require
+you to use multiple source files, one for each CPU configuration.
+Because stb_image is a header-file library that compiles in only
+one source file, there's no approved way to build both an
+SSE-enabled and a non-SSE-enabled variation.
+
+While we've tried to work around it, we've had multiple issues over
+the years due to specific versions of gcc breaking what we're doing,
+so we've given up on it. See https://github.com/nothings/stb/issues/280
+and https://github.com/nothings/stb/issues/410 for examples.
+
+#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow?
 
 Generally they're only better in that they're easier to integrate,
 easier to use, and easier to release (single file; good API; no
@@ -51,6 +82,10 @@ attribution requirement). They may be less featureful, slower,
 and/or use more memory. If you're already using an equivalent
 library, there's probably no good reason to switch.
 
+#### Can I link directly to the table of stb libraries?
+
+You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list.
+
 #### Why do you list "lines of code"? It's a terrible metric.
 
 Just to give you some idea of the internal complexity of the library,
@@ -88,10 +123,10 @@ remember to attach *two* files, etc.
 #### Why "stb"? Is this something to do with Set-Top Boxes?
 
 No, they are just the initials for my name, Sean T. Barrett.
-This was not chosen out of egomania, but as a semi-robust
+This was not chosen out of egomania, but as a moderately sane
 way of namespacing the filenames and source function names.
 
-#### Will you add more image types to stb_image.c?
+#### Will you add more image types to stb_image.h?
 
 If people submit them, I generally add them, but the goal of stb_image
 is less for applications like image viewer apps (which need to support
@@ -99,10 +134,6 @@ every type of image under the sun) and more for things like games which
 can choose what images to use, so I may decline to add them if they're
 too rare or if the size of implementation vs. apparent benefit is too low.
 
-#### Are there other single-file public-domain libraries out there?
-
-Yes. I'll put a list here when people remind me what they are.
-
 #### Do you have any advice on how to create my own single-file library?
 
 Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt

二進制
data/map_01.png


二進制
data/map_02.png


二進制
data/map_03.png


+ 1055 - 0
deprecated/rrsprintf.h

@@ -0,0 +1,1055 @@
+#ifndef RR_SPRINTF_H_INCLUDE
+#define RR_SPRINTF_H_INCLUDE
+
+/*
+Single file sprintf replacement.
+
+Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. 
+Hereby placed in public domain.
+
+This is a full sprintf replacement that supports everything that
+the C runtime sprintfs support, including float/double, 64-bit integers,
+hex floats, field parameters (%*.*d stuff), length reads backs, etc.
+
+Why would you need this if sprintf already exists?  Well, first off,
+it's *much* faster (see below). It's also much smaller than the CRT
+versions code-space-wise. We've also added some simple improvements 
+that are super handy (commas in thousands, callbacks at buffer full,
+for example). Finally, the format strings for MSVC and GCC differ 
+for 64-bit integers (among other small things), so this lets you use 
+the same format strings in cross platform code.
+
+It uses the standard single file trick of being both the header file
+and the source itself. If you just include it normally, you just get 
+the header file function definitions. To get the code, you include
+it from a C or C++ file and define RR_SPRINTF_IMPLEMENTATION first.
+
+It only uses va_args macros from the C runtime to do it's work. It
+does cast doubles to S64s and shifts and divides U64s, which does 
+drag in CRT code on most platforms.
+
+It compiles to roughly 8K with float support, and 4K without.
+As a comparison, when using MSVC static libs, calling sprintf drags
+in 16K.
+
+API:
+====
+int rrsprintf( char * buf, char const * fmt, ... )
+int rrsnprintf( char * buf, int count, char const * fmt, ... )
+  Convert an arg list into a buffer.  rrsnprintf always returns
+  a zero-terminated string (unlike regular snprintf).
+
+int rrvsprintf( char * buf, char const * fmt, va_list va )
+int rrvsnprintf( char * buf, int count, char const * fmt, va_list va )
+  Convert a va_list arg list into a buffer.  rrvsnprintf always returns
+  a zero-terminated string (unlike regular snprintf).
+
+int rrvsprintfcb( RRSPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
+    typedef char * RRSPRINTFCB( char const * buf, void * user, int len );
+  Convert into a buffer, calling back every RR_SPRINTF_MIN chars.
+  Your callback can then copy the chars out, print them or whatever.
+  This function is actually the workhorse for everything else.
+  The buffer you pass in must hold at least RR_SPRINTF_MIN characters.
+    // you return the next buffer to use or 0 to stop converting
+
+void rrsetseparators( char comma, char period )
+  Set the comma and period characters to use.
+
+FLOATS/DOUBLES:
+===============
+This code uses a internal float->ascii conversion method that uses
+doubles with error correction (double-doubles, for ~105 bits of
+precision).  This conversion is round-trip perfect - that is, an atof
+of the values output here will give you the bit-exact double back.
+
+One difference is that our insignificant digits will be different than 
+with MSVC or GCC (but they don't match each other either).  We also 
+don't attempt to find the minimum length matching float (pre-MSVC15 
+doesn't either).
+
+If you don't need float or doubles at all, define RR_SPRINTF_NOFLOAT
+and you'll save 4K of code space.
+
+64-BIT INTS:
+============
+This library also supports 64-bit integers and you can use MSVC style or
+GCC style indicators (%I64d or %lld).  It supports the C99 specifiers
+for size_t and ptr_diff_t (%jd %zd) as well.
+
+EXTRAS:
+=======
+Like some GCCs, for integers and floats, you can use a ' (single quote)
+specifier and commas will be inserted on the thousands: "%'d" on 12345 
+would print 12,345.
+
+For integers and floats, you can use a "$" specifier and the number 
+will be converted to float and then divided to get kilo, mega, giga or
+tera and then printed, so "%$d" 1024 is "1.0 k", "%$.2d" 2536000 is 
+"2.42 m", etc.
+
+In addition to octal and hexadecimal conversions, you can print 
+integers in binary: "%b" for 256 would print 100.
+
+PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
+===================================================================
+"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
+"%24d" across all 32-bit ints (4.5x/4.2x faster)
+"%x" across all 32-bit ints (4.5x/3.8x faster)
+"%08x" across all 32-bit ints (4.3x/3.8x faster)
+"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
+"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
+"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
+"%f" for values near e-300 (7.9x/6.5x faster)
+"%f" for values near e+300 (10.0x/9.1x faster)
+"%e" for values near e-300 (10.1x/7.0x faster)
+"%e" for values near e+300 (9.2x/6.0x faster)
+"%.320f" for values near e-300 (12.6x/11.2x faster)
+"%a" for random values (8.6x/4.3x faster)
+"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
+"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
+"%s%s%s" for 64 char strings (7.1x/7.3x faster)
+"...512 char string..." ( 35.0x/32.5x faster!)
+*/
+
+#ifdef RR_SPRINTF_STATIC
+#define RRPUBLIC_DEC static
+#define RRPUBLIC_DEF static
+#else
+#ifdef __cplusplus
+#define RRPUBLIC_DEC extern "C"
+#define RRPUBLIC_DEF extern "C"
+#else
+#define RRPUBLIC_DEC extern 
+#define RRPUBLIC_DEF
+#endif
+#endif
+
+#include <stdarg.h>  // for va_list()
+
+#ifndef RR_SPRINTF_MIN
+#define RR_SPRINTF_MIN 512 // how many characters per callback
+#endif
+typedef char * RRSPRINTFCB( char * buf, void * user, int len );
+
+#ifndef RR_SPRINTF_DECORATE
+#define RR_SPRINTF_DECORATE(name) rr##name  // define this before including if you want to change the names
+#endif
+
+#ifndef RR_SPRINTF_IMPLEMENTATION
+
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsprintf )( char * buf, char const * fmt, va_list va );
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va );
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( sprintf ) ( char * buf, char const * fmt, ... );
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( snprintf )( char * buf, int count, char const * fmt, ... );
+
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsprintfcb )( RRSPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va );
+RRPUBLIC_DEF void RR_SPRINTF_DECORATE( setseparators )( char comma, char period );
+
+#else
+
+#include <stdlib.h>  // for va_arg()
+
+#define rU32 unsigned int
+#define rS32 signed int
+
+#ifdef _MSC_VER
+#define rU64 unsigned __int64
+#define rS64 signed __int64
+#else
+#define rU64 unsigned long long
+#define rS64 signed long long
+#endif
+#define rU16 unsigned short
+
+#ifndef rUINTa 
+#if defined(__ppc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
+#define rUINTa rU64
+#else
+#define rUINTa rU32
+#endif
+#endif
+
+#ifndef RR_SPRINTF_MSVC_MODE  // used for MSVC2013 and earlier (MSVC2015 matches GCC)
+#if defined(_MSC_VER) && (_MSC_VER<1900)
+#define RR_SPRINTF_MSVC_MODE
+#endif
+#endif
+
+#ifdef RR_SPRINTF_NOUNALIGNED  // define this before inclusion to force rrsprint to always use aligned accesses
+#define RR_UNALIGNED(code)
+#else
+#define RR_UNALIGNED(code) code
+#endif
+
+#ifndef RR_SPRINTF_NOFLOAT
+// internal float utility functions
+static rS32 rrreal_to_str( char const * * start, rU32 * len, char *out, rS32 * decimal_pos, double value, rU32 frac_digits );
+static rS32 rrreal_to_parts( rS64 * bits, rS32 * expo, double value );
+#define RRSPECIAL 0x7000
+#endif
+
+static char RRperiod='.';
+static char RRcomma=',';
+static char rrdiglookup[201]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
+
+RRPUBLIC_DEF void RR_SPRINTF_DECORATE( setseparators )( char pcomma, char pperiod )
+{
+  RRperiod=pperiod;
+  RRcomma=pcomma;
+}
+
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsprintfcb )( RRSPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
+{
+  static char hex[]="0123456789abcdefxp";
+  static char hexu[]="0123456789ABCDEFXP";
+  char * bf;
+  char const * f;
+  int tlen = 0;
+
+  bf = buf;
+  f = fmt;
+  for(;;)
+  {
+    rS32 fw,pr,tz; rU32 fl;
+
+    #define LJ 1
+    #define LP 2
+    #define LS 4
+    #define LX 8
+    #define LZ 16
+    #define BI 32
+    #define CS 64
+    #define NG 128
+    #define KI 256
+    #define HW 512
+ 
+    // macros for the callback buffer stuff
+    #define chk_cb_bufL(bytes) { int len = (int)(bf-buf); if ((len+(bytes))>=RR_SPRINTF_MIN) { tlen+=len; if (0==(bf=buf=callback(buf,user,len))) goto done; } }
+    #define chk_cb_buf(bytes) { if ( callback ) { chk_cb_bufL(bytes); } }
+    #define flush_cb() { chk_cb_bufL(RR_SPRINTF_MIN-1); } //flush if there is even one byte in the buffer
+    #define cb_buf_clamp(cl,v) cl = v; if ( callback ) { int lg = RR_SPRINTF_MIN-(int)(bf-buf); if (cl>lg) cl=lg; }
+
+    // fast copy everything up to the next % (or end of string)
+    for(;;)
+    { 
+      while (((rUINTa)f)&3)
+      {
+       schk1: if (f[0]=='%') goto scandd;
+       schk2: if (f[0]==0) goto endfmt;
+        chk_cb_buf(1); *bf++=f[0]; ++f;
+      } 
+      for(;;)
+      { 
+        rU32 v,c;
+        v=*(rU32*)f; c=(~v)&0x80808080;
+        if ((v-0x26262626)&c) goto schk1; 
+        if ((v-0x01010101)&c) goto schk2; 
+        if (callback) if ((RR_SPRINTF_MIN-(int)(bf-buf))<4) goto schk1;
+        *(rU32*)bf=v; bf+=4; f+=4;
+      }
+    } scandd:
+
+    ++f;
+
+    // ok, we have a percent, read the modifiers first
+    fw = 0; pr = -1; fl = 0; tz = 0;
+    
+    // flags
+    for(;;)
+    {
+      switch(f[0])
+      {
+        // if we have left just
+        case '-': fl|=LJ; ++f; continue;
+        // if we have leading plus
+        case '+': fl|=LP; ++f; continue; 
+        // if we have leading space
+        case ' ': fl|=LS; ++f; continue; 
+        // if we have leading 0x
+        case '#': fl|=LX; ++f; continue; 
+        // if we have thousand commas
+        case '\'': fl|=CS; ++f; continue; 
+        // if we have kilo marker
+        case '$': fl|=KI; ++f; continue; 
+        // if we have leading zero
+        case '0': fl|=LZ; ++f; goto flags_done; 
+        default: goto flags_done;
+      }
+    }
+    flags_done:
+   
+    // get the field width
+    if ( f[0] == '*' ) {fw = va_arg(va,rU32); ++f;} else { while (( f[0] >= '0' ) && ( f[0] <= '9' )) { fw = fw * 10 + f[0] - '0'; f++; } }
+    // get the precision
+    if ( f[0]=='.' ) { ++f; if ( f[0] == '*' ) {pr = va_arg(va,rU32); ++f;} else { pr = 0; while (( f[0] >= '0' ) && ( f[0] <= '9' )) { pr = pr * 10 + f[0] - '0'; f++; } } } 
+    
+    // handle integer size overrides
+    switch(f[0])
+    {
+      // are we halfwidth?
+      case 'h': fl|=HW; ++f; break;
+      // are we 64-bit (unix style)
+      case 'l': ++f; if ( f[0]=='l') { fl|=BI; ++f; } break;
+      // are we 64-bit on intmax? (c99)
+      case 'j': fl|=BI; ++f; break; 
+      // are we 64-bit on size_t or ptrdiff_t? (c99)
+      case 'z': case 't': fl|=((sizeof(char*)==8)?BI:0); ++f; break; 
+      // are we 64-bit (msft style)
+      case 'I': if ( ( f[1]=='6') && ( f[2]=='4') ) { fl|=BI; f+=3; } else if ( ( f[1]=='3') && ( f[2]=='2') ) { f+=3; } else { fl|=((sizeof(void*)==8)?BI:0); ++f; } break;
+      default: break;
+    }
+
+    // handle each replacement
+    switch( f[0] )
+    {
+      #define NUMSZ 512 // big enough for e308 (with commas) or e-307 
+      char num[NUMSZ]; 
+      char lead[8]; 
+      char tail[8]; 
+      char *s;
+      char const *h;
+      rU32 l,n,cs;
+      rU64 n64;
+      #ifndef RR_SPRINTF_NOFLOAT      
+      double fv; 
+      #endif
+      rS32 dp; char const * sn;
+
+      case 's':
+        // get the string
+        s = va_arg(va,char*); if (s==0) s = (char*)"null";
+        // get the length
+        sn = s;
+        for(;;)
+        { 
+          if ((((rUINTa)sn)&3)==0) break;
+         lchk:
+          if (sn[0]==0) goto ld;
+          ++sn;
+        }
+        n = 0xffffffff;
+        if (pr>=0) { n=(rU32)(sn-s); if (n>=(rU32)pr) goto ld; n=((rU32)(pr-n))>>2; }
+        while(n) 
+        { 
+          rU32 v=*(rU32*)sn;
+          if ((v-0x01010101)&(~v)&0x80808080UL) goto lchk; 
+          sn+=4; 
+          --n;
+        }
+        goto lchk;
+       ld:
+
+        l = (rU32) ( sn - s );
+        // clamp to precision
+        if ( l > (rU32)pr ) l = pr;
+        lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
+        // copy the string in
+        goto scopy;
+
+      case 'c': // char
+        // get the character
+        s = num + NUMSZ -1; *s = (char)va_arg(va,int);
+        l = 1;
+        lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
+        goto scopy;
+
+      case 'n': // weird write-bytes specifier
+        { int * d = va_arg(va,int*);
+        *d = tlen + (int)( bf - buf ); }
+        break;
+
+#ifdef RR_SPRINTF_NOFLOAT
+      case 'A': // float
+      case 'a': // hex float
+      case 'G': // float
+      case 'g': // float
+      case 'E': // float
+      case 'e': // float
+      case 'f': // float
+        va_arg(va,double); // eat it
+        s = (char*)"No float";
+        l = 8;
+        lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
+        goto scopy;
+#else
+      case 'A': // float
+        h=hexu;  
+        goto hexfloat;
+
+      case 'a': // hex float
+        h=hex;
+       hexfloat: 
+        fv = va_arg(va,double);
+        if (pr==-1) pr=6; // default is 6
+        // read the double into a string
+        if ( rrreal_to_parts( (rS64*)&n64, &dp, fv ) )
+          fl |= NG;
+  
+        s = num+64;
+
+        // sign
+        lead[0]=0; if (fl&NG) { lead[0]=1; lead[1]='-'; } else if (fl&LS) { lead[0]=1; lead[1]=' '; } else if (fl&LP) { lead[0]=1; lead[1]='+'; };
+
+        if (dp==-1023) dp=(n64)?-1022:0; else n64|=(((rU64)1)<<52);
+        n64<<=(64-56);
+        if (pr<15) n64+=((((rU64)8)<<56)>>(pr*4));
+        // add leading chars
+        
+        #ifdef RR_SPRINTF_MSVC_MODE
+        *s++='0';*s++='x';
+        #else
+        lead[1+lead[0]]='0'; lead[2+lead[0]]='x'; lead[0]+=2;
+        #endif
+        *s++=h[(n64>>60)&15]; n64<<=4;
+        if ( pr ) *s++=RRperiod;
+        sn = s;
+
+        // print the bits
+        n = pr; if (n>13) n = 13; if (pr>(rS32)n) tz=pr-n; pr = 0;
+        while(n--) { *s++=h[(n64>>60)&15]; n64<<=4; }
+
+        // print the expo
+        tail[1]=h[17];
+        if (dp<0) { tail[2]='-'; dp=-dp;} else tail[2]='+';
+        n = (dp>=1000)?6:((dp>=100)?5:((dp>=10)?4:3));
+        tail[0]=(char)n;
+        for(;;) { tail[n]='0'+dp%10; if (n<=3) break; --n; dp/=10; }
+
+        dp = (int)(s-sn);
+        l = (int)(s-(num+64));
+        s = num+64;
+        cs = 1 + (3<<24);
+        goto scopy;
+
+      case 'G': // float
+        h=hexu;
+        goto dosmallfloat;
+
+      case 'g': // float
+        h=hex;
+       dosmallfloat:   
+        fv = va_arg(va,double);
+        if (pr==-1) pr=6; else if (pr==0) pr = 1; // default is 6
+        // read the double into a string
+        if ( rrreal_to_str( &sn, &l, num, &dp, fv, (pr-1)|0x80000000 ) )
+          fl |= NG;
+
+        // clamp the precision and delete extra zeros after clamp
+        n = pr;
+        if ( l > (rU32)pr ) l = pr; while ((l>1)&&(pr)&&(sn[l-1]=='0')) { --pr; --l; }
+
+        // should we use %e
+        if ((dp<=-4)||(dp>(rS32)n))
+        {
+          if ( pr > (rS32)l ) pr = l-1; else if ( pr ) --pr; // when using %e, there is one digit before the decimal
+          goto doexpfromg;
+        }
+        // this is the insane action to get the pr to match %g sematics for %f
+        if(dp>0) { pr=(dp<(rS32)l)?l-dp:0; } else { pr = -dp+((pr>(rS32)l)?l:pr); }
+        goto dofloatfromg;
+
+      case 'E': // float
+        h=hexu;  
+        goto doexp;
+
+      case 'e': // float
+        h=hex;
+       doexp:   
+        fv = va_arg(va,double);
+        if (pr==-1) pr=6; // default is 6
+        // read the double into a string
+        if ( rrreal_to_str( &sn, &l, num, &dp, fv, pr|0x80000000 ) )
+          fl |= NG;
+       doexpfromg: 
+        tail[0]=0; 
+        lead[0]=0; if (fl&NG) { lead[0]=1; lead[1]='-'; } else if (fl&LS) { lead[0]=1; lead[1]=' '; } else if (fl&LP) { lead[0]=1; lead[1]='+'; };
+        if ( dp == RRSPECIAL ) { s=(char*)sn; cs=0; pr=0; goto scopy; }
+        s=num+64; 
+        // handle leading chars
+        *s++=sn[0];
+
+        if (pr) *s++=RRperiod;
+
+        // handle after decimal
+        if ((l-1)>(rU32)pr) l=pr+1;
+        for(n=1;n<l;n++) *s++=sn[n];
+        // trailing zeros
+        tz = pr-(l-1); pr=0;
+        // dump expo
+        tail[1]=h[0xe];
+        dp -= 1;
+        if (dp<0) { tail[2]='-'; dp=-dp;} else tail[2]='+';
+        #ifdef RR_SPRINTF_MSVC_MODE
+        n = 5;
+        #else
+        n = (dp>=100)?5:4;
+        #endif
+        tail[0]=(char)n;
+        for(;;) { tail[n]='0'+dp%10; if (n<=3) break; --n; dp/=10; }
+        cs = 1 + (3<<24); // how many tens
+        goto flt_lead;   
+
+      case 'f': // float
+        fv = va_arg(va,double);
+       doafloat: 
+        // do kilos
+        if (fl&KI) {while(fl<0x4000000) { if ((fv<1024.0) && (fv>-1024.0)) break; fv/=1024.0; fl+=0x1000000; }} 
+        if (pr==-1) pr=6; // default is 6
+        // read the double into a string
+        if ( rrreal_to_str( &sn, &l, num, &dp, fv, pr ) )
+          fl |= NG;
+        dofloatfromg:
+        tail[0]=0;
+        // sign
+        lead[0]=0; if (fl&NG) { lead[0]=1; lead[1]='-'; } else if (fl&LS) { lead[0]=1; lead[1]=' '; } else if (fl&LP) { lead[0]=1; lead[1]='+'; };
+        if ( dp == RRSPECIAL ) { s=(char*)sn; cs=0; pr=0; goto scopy; }
+        s=num+64; 
+
+        // handle the three decimal varieties
+        if (dp<=0) 
+        { 
+          rS32 i;
+          // handle 0.000*000xxxx
+          *s++='0'; if (pr) *s++=RRperiod; 
+          n=-dp; if((rS32)n>pr) n=pr; i=n; while(i) { if ((((rUINTa)s)&3)==0) break; *s++='0'; --i; } while(i>=4) { *(rU32*)s=0x30303030; s+=4; i-=4; } while(i) { *s++='0'; --i; }
+          if ((rS32)(l+n)>pr) l=pr-n; i=l; while(i) { *s++=*sn++; --i; }
+          tz = pr-(n+l);
+          cs = 1 + (3<<24); // how many tens did we write (for commas below)
+        }
+        else
+        {
+          cs = (fl&CS)?((600-(rU32)dp)%3):0;
+          if ((rU32)dp>=l)
+          {
+            // handle xxxx000*000.0
+            n=0; for(;;) { if ((fl&CS) && (++cs==4)) { cs = 0; *s++=RRcomma; } else { *s++=sn[n]; ++n; if (n>=l) break; } }
+            if (n<(rU32)dp)
+            {
+              n = dp - n;
+              if ((fl&CS)==0) { while(n) { if ((((rUINTa)s)&3)==0) break; *s++='0'; --n; }  while(n>=4) { *(rU32*)s=0x30303030; s+=4; n-=4; } }
+              while(n) { if ((fl&CS) && (++cs==4)) { cs = 0; *s++=RRcomma; } else { *s++='0'; --n; } }
+            }
+            cs = (int)(s-(num+64)) + (3<<24); // cs is how many tens
+            if (pr) { *s++=RRperiod; tz=pr;}
+          }
+          else
+          {
+            // handle xxxxx.xxxx000*000
+            n=0; for(;;) { if ((fl&CS) && (++cs==4)) { cs = 0; *s++=RRcomma; } else { *s++=sn[n]; ++n; if (n>=(rU32)dp) break; } }
+            cs = (int)(s-(num+64)) + (3<<24); // cs is how many tens
+            if (pr) *s++=RRperiod;
+            if ((l-dp)>(rU32)pr) l=pr+dp;
+            while(n<l) { *s++=sn[n]; ++n; }
+            tz = pr-(l-dp);
+          }
+        }
+        pr = 0;
+        
+        // handle k,m,g,t
+        if (fl&KI) { tail[0]=1; tail[1]=' '; { if (fl>>24) { tail[2]="_kmgt"[fl>>24]; tail[0]=2; } } };
+
+        flt_lead:
+        // get the length that we copied
+        l = (rU32) ( s-(num+64) );
+        s=num+64; 
+        goto scopy;
+#endif
+
+      case 'B': // upper binary
+        h = hexu;
+        goto binary;
+
+      case 'b': // lower binary
+        h = hex;
+        binary:
+        lead[0]=0;
+        if (fl&LX) { lead[0]=2;lead[1]='0';lead[2]=h[0xb]; }
+        l=(8<<4)|(1<<8);
+        goto radixnum;
+
+      case 'o': // octal
+        h = hexu;
+        lead[0]=0;
+        if (fl&LX) { lead[0]=1;lead[1]='0'; }
+        l=(3<<4)|(3<<8);
+        goto radixnum;
+
+      case 'p': // pointer
+        fl |= (sizeof(void*)==8)?BI:0;
+        pr = sizeof(void*)*2;
+        fl &= ~LZ; // 'p' only prints the pointer with zeros
+        // drop through to X
+      
+      case 'X': // upper binary
+        h = hexu;
+        goto dohexb;
+
+      case 'x': // lower binary
+        h = hex; dohexb:
+        l=(4<<4)|(4<<8);
+        lead[0]=0;
+        if (fl&LX) { lead[0]=2;lead[1]='0';lead[2]=h[16]; }
+       radixnum: 
+        // get the number
+        if ( fl&BI )
+          n64 = va_arg(va,rU64);
+        else
+          n64 = va_arg(va,rU32);
+
+        s = num + NUMSZ; dp = 0;
+        // clear tail, and clear leading if value is zero
+        tail[0]=0; if (n64==0) { lead[0]=0; if (pr==0) { l=0; cs = ( ((l>>4)&15)) << 24; goto scopy; } }
+        // convert to string
+        for(;;) { *--s = h[n64&((1<<(l>>8))-1)]; n64>>=(l>>8); if ( ! ( (n64) || ((rS32) ( (num+NUMSZ) - s ) < pr ) ) ) break; if ( fl&CS) { ++l; if ((l&15)==((l>>4)&15)) { l&=~15; *--s=RRcomma; } } };
+        // get the tens and the comma pos
+        cs = (rU32) ( (num+NUMSZ) - s ) + ( ( ((l>>4)&15)) << 24 );
+        // get the length that we copied
+        l = (rU32) ( (num+NUMSZ) - s );
+        // copy it
+        goto scopy;
+
+      case 'u': // unsigned
+      case 'i':
+      case 'd': // integer
+        // get the integer and abs it
+        if ( fl&BI )
+        {
+          rS64 i64 = va_arg(va,rS64); n64 = (rU64)i64; if ((f[0]!='u') && (i64<0)) { n64=(rU64)-i64; fl|=NG; }
+        }
+        else
+        {
+          rS32 i = va_arg(va,rS32); n64 = (rU32)i; if ((f[0]!='u') && (i<0)) { n64=(rU32)-i; fl|=NG; }
+        }
+
+        #ifndef RR_SPRINTF_NOFLOAT
+        if (fl&KI) { if (n64<1024) pr=0; else if (pr==-1) pr=1; fv=(double)(rS64)n64; goto doafloat; } 
+        #endif
+
+        // convert to string
+        s = num+NUMSZ; l=0; 
+        
+        for(;;)
+        {
+          // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
+          char * o=s-8;
+          if (n64>=100000000) { n = (rU32)( n64 % 100000000);  n64 /= 100000000; } else {n = (rU32)n64; n64 = 0; }
+          if((fl&CS)==0) { while(n) { s-=2; *(rU16*)s=*(rU16*)&rrdiglookup[(n%100)*2]; n/=100; } }
+          while (n) { if ( ( fl&CS) && (l++==3) ) { l=0; *--s=RRcomma; --o; } else { *--s=(char)(n%10)+'0'; n/=10; } }
+          if (n64==0) { if ((s[0]=='0') && (s!=(num+NUMSZ))) ++s; break; }
+          while (s!=o) if ( ( fl&CS) && (l++==3) ) { l=0; *--s=RRcomma; --o; } else { *--s='0'; }
+        }
+
+        tail[0]=0;
+        // sign
+        lead[0]=0; if (fl&NG) { lead[0]=1; lead[1]='-'; } else if (fl&LS) { lead[0]=1; lead[1]=' '; } else if (fl&LP) { lead[0]=1; lead[1]='+'; };
+
+        // get the length that we copied
+        l = (rU32) ( (num+NUMSZ) - s ); if ( l == 0 ) { *--s='0'; l = 1; }
+        cs = l + (3<<24);
+        if (pr<0) pr = 0;
+
+       scopy: 
+        // get fw=leading/trailing space, pr=leading zeros
+        if (pr<(rS32)l) pr = l;
+        n = pr + lead[0] + tail[0] + tz;
+        if (fw<(rS32)n) fw = n;
+        fw -= n;
+        pr -= l;
+
+        // handle right justify and leading zeros
+        if ( (fl&LJ)==0 )
+        {
+          if (fl&LZ) // if leading zeros, everything is in pr
+          { 
+            pr = (fw>pr)?fw:pr;
+            fw = 0;
+          }
+          else
+          {
+            fl &= ~CS; // if no leading zeros, then no commas
+          }
+        }
+
+        // copy the spaces and/or zeros
+        if (fw+pr)
+        {
+          rS32 i; rU32 c; 
+
+          // copy leading spaces (or when doing %8.4d stuff)
+          if ( (fl&LJ)==0 ) while(fw>0) { cb_buf_clamp(i,fw); fw -= i; while(i) { if ((((rUINTa)bf)&3)==0) break; *bf++=' '; --i; } while(i>=4) { *(rU32*)bf=0x20202020; bf+=4; i-=4; } while (i) {*bf++=' '; --i;} chk_cb_buf(1); }
+        
+          // copy leader
+          sn=lead+1; while(lead[0]) { cb_buf_clamp(i,lead[0]); lead[0] -= (char)i; while (i) {*bf++=*sn++; --i;} chk_cb_buf(1); }
+          
+          // copy leading zeros
+          c = cs >> 24; cs &= 0xffffff;
+          cs = (fl&CS)?((rU32)(c-((pr+cs)%(c+1)))):0;
+          while(pr>0) { cb_buf_clamp(i,pr); pr -= i; if((fl&CS)==0) { while(i) { if ((((rUINTa)bf)&3)==0) break; *bf++='0'; --i; } while(i>=4) { *(rU32*)bf=0x30303030; bf+=4; i-=4; } } while (i) { if((fl&CS) && (cs++==c)) { cs = 0; *bf++=RRcomma; } else *bf++='0'; --i; } chk_cb_buf(1); }
+        }
+
+        // copy leader if there is still one
+        sn=lead+1; while(lead[0]) { rS32 i; cb_buf_clamp(i,lead[0]); lead[0] -= (char)i; while (i) {*bf++=*sn++; --i;} chk_cb_buf(1); }
+
+        // copy the string
+        n = l; while (n) { rS32 i; cb_buf_clamp(i,n); n-=i; RR_UNALIGNED( while(i>=4) { *(rU32*)bf=*(rU32*)s; bf+=4; s+=4; i-=4; } ) while (i) {*bf++=*s++; --i;} chk_cb_buf(1); }
+
+        // copy trailing zeros
+        while(tz) { rS32 i; cb_buf_clamp(i,tz); tz -= i; while(i) { if ((((rUINTa)bf)&3)==0) break; *bf++='0'; --i; } while(i>=4) { *(rU32*)bf=0x30303030; bf+=4; i-=4; } while (i) {*bf++='0'; --i;} chk_cb_buf(1); }
+
+        // copy tail if there is one
+        sn=tail+1; while(tail[0]) { rS32 i; cb_buf_clamp(i,tail[0]); tail[0] -= (char)i; while (i) {*bf++=*sn++; --i;} chk_cb_buf(1); }
+
+        // handle the left justify
+        if (fl&LJ) if (fw>0) { while (fw) { rS32 i; cb_buf_clamp(i,fw); fw-=i; while(i) { if ((((rUINTa)bf)&3)==0) break; *bf++=' '; --i; } while(i>=4) { *(rU32*)bf=0x20202020; bf+=4; i-=4; } while (i--) *bf++=' '; chk_cb_buf(1); } }
+        break;
+
+      default: // unknown, just copy code
+        s = num + NUMSZ -1; *s = f[0];
+        l = 1;
+        fw=pr=fl=0;
+        lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
+        goto scopy;
+    }
+    ++f;
+  }
+ endfmt:
+
+  if (!callback) 
+    *bf = 0;
+  else
+    flush_cb();
+ 
+ done:
+  return tlen + (int)(bf-buf);
+}
+
+// cleanup
+#undef LJ
+#undef LP
+#undef LS
+#undef LX
+#undef LZ
+#undef BI
+#undef CS
+#undef NG
+#undef KI
+#undef NUMSZ
+#undef chk_cb_bufL
+#undef chk_cb_buf
+#undef flush_cb
+#undef cb_buf_clamp
+
+// ============================================================================
+//   wrapper functions
+
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( sprintf )( char * buf, char const * fmt, ... )
+{
+  va_list va;
+  va_start( va, fmt );
+  return RR_SPRINTF_DECORATE( vsprintfcb )( 0, 0, buf, fmt, va );
+}
+
+typedef struct RRCCS
+{
+  char * buf;
+  int count;
+  char tmp[ RR_SPRINTF_MIN ];
+} RRCCS;
+
+static char * rrclampcallback( char * buf, void * user, int len )
+{
+  RRCCS * c = (RRCCS*)user;
+
+  if ( len > c->count ) len = c->count;
+
+  if (len)
+  {
+    if ( buf != c->buf )
+    {
+      char * s, * d, * se;
+      d = c->buf; s = buf; se = buf+len;
+      do{ *d++ = *s++; } while (s<se);
+    }
+    c->buf += len;
+    c->count -= len;
+  }
+  
+  if ( c->count <= 0 ) return 0;
+  return ( c->count >= RR_SPRINTF_MIN ) ? c->buf : c->tmp; // go direct into buffer if you can
+}
+
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )
+{
+  RRCCS c;
+  int l;
+
+  if ( count == 0 )
+    return 0;
+
+  c.buf = buf;
+  c.count = count;
+
+  RR_SPRINTF_DECORATE( vsprintfcb )( rrclampcallback, &c, rrclampcallback(0,&c,0), fmt, va );
+  
+  // zero-terminate
+  l = (int)( c.buf - buf );
+  if ( l >= count ) // should never be greater, only equal (or less) than count
+    l = count - 1;
+  buf[l] = 0;
+
+  return l;
+}
+
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( snprintf )( char * buf, int count, char const * fmt, ... )
+{
+  va_list va;
+  va_start( va, fmt );
+
+  return RR_SPRINTF_DECORATE( vsnprintf )( buf, count, fmt, va );
+}
+
+RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsprintf )( char * buf, char const * fmt, va_list va )
+{
+  return RR_SPRINTF_DECORATE( vsprintfcb )( 0, 0, buf, fmt, va );
+}
+
+// =======================================================================
+//   low level float utility functions
+
+#ifndef RR_SPRINTF_NOFLOAT
+
+ // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
+ #define RRCOPYFP(dest,src) { int cn; for(cn=0;cn<8;cn++) ((char*)&dest)[cn]=((char*)&src)[cn]; }
+ 
+// get float info
+static rS32 rrreal_to_parts( rS64 * bits, rS32 * expo, double value )
+{
+  double d;
+  rS64 b = 0;
+
+  // load value and round at the frac_digits
+  d = value;
+
+  RRCOPYFP( b, d );
+
+  *bits = b & ((((rU64)1)<<52)-1);
+  *expo = ((b >> 52) & 2047)-1023;
+    
+  return (rS32)(b >> 63);
+}
+
+static double const rrbot[23]={1e+000,1e+001,1e+002,1e+003,1e+004,1e+005,1e+006,1e+007,1e+008,1e+009,1e+010,1e+011,1e+012,1e+013,1e+014,1e+015,1e+016,1e+017,1e+018,1e+019,1e+020,1e+021,1e+022};
+static double const rrnegbot[22]={1e-001,1e-002,1e-003,1e-004,1e-005,1e-006,1e-007,1e-008,1e-009,1e-010,1e-011,1e-012,1e-013,1e-014,1e-015,1e-016,1e-017,1e-018,1e-019,1e-020,1e-021,1e-022};
+static double const rrnegboterr[22]={-5.551115123125783e-018,-2.0816681711721684e-019,-2.0816681711721686e-020,-4.7921736023859299e-021,-8.1803053914031305e-022,4.5251888174113741e-023,4.5251888174113739e-024,-2.0922560830128471e-025,-6.2281591457779853e-026,-3.6432197315497743e-027,6.0503030718060191e-028,2.0113352370744385e-029,-3.0373745563400371e-030,1.1806906454401013e-032,-7.7705399876661076e-032,2.0902213275965398e-033,-7.1542424054621921e-034,-7.1542424054621926e-035,2.4754073164739869e-036,5.4846728545790429e-037,9.2462547772103625e-038,-4.8596774326570872e-039};
+static double const rrtop[13]={1e+023,1e+046,1e+069,1e+092,1e+115,1e+138,1e+161,1e+184,1e+207,1e+230,1e+253,1e+276,1e+299};
+static double const rrnegtop[13]={1e-023,1e-046,1e-069,1e-092,1e-115,1e-138,1e-161,1e-184,1e-207,1e-230,1e-253,1e-276,1e-299};
+static double const rrtoperr[13]={8388608,6.8601809640529717e+028,-7.253143638152921e+052,-4.3377296974619174e+075,-1.5559416129466825e+098,-3.2841562489204913e+121,-3.7745893248228135e+144,-1.7356668416969134e+167,-3.8893577551088374e+190,-9.9566444326005119e+213,6.3641293062232429e+236,-5.2069140800249813e+259,-5.2504760255204387e+282};
+static double const rrnegtoperr[13]={3.9565301985100693e-040,-2.299904345391321e-063,3.6506201437945798e-086,1.1875228833981544e-109,-5.0644902316928607e-132,-6.7156837247865426e-155,-2.812077463003139e-178,-5.7778912386589953e-201,7.4997100559334532e-224,-4.6439668915134491e-247,-6.3691100762962136e-270,-9.436808465446358e-293,8.0970921678014997e-317};
+
+#if defined(_MSC_VER) && (_MSC_VER<=1200)                                                                                                                                                                                       
+static rU64 const rrpot[20]={1,10,100,1000, 10000,100000,1000000,10000000, 100000000,1000000000,10000000000,100000000000,  1000000000000,10000000000000,100000000000000,1000000000000000,  10000000000000000,100000000000000000,1000000000000000000,10000000000000000000U };
+#define rrtento19th ((rU64)1000000000000000000)
+#else
+static rU64 const rrpot[20]={1,10,100,1000, 10000,100000,1000000,10000000, 100000000,1000000000,10000000000ULL,100000000000ULL,  1000000000000ULL,10000000000000ULL,100000000000000ULL,1000000000000000ULL,  10000000000000000ULL,100000000000000000ULL,1000000000000000000ULL,10000000000000000000ULL };
+#define rrtento19th (1000000000000000000ULL)
+#endif
+
+#define rrddmulthi(oh,ol,xh,yh) \
+{ \
+  double ahi=0,alo,bhi=0,blo; \
+  rS64 bt; \
+  oh = xh * yh; \
+  RRCOPYFP(bt,xh); bt&=((~(rU64)0)<<27); RRCOPYFP(ahi,bt); alo = xh-ahi; \
+  RRCOPYFP(bt,yh); bt&=((~(rU64)0)<<27); RRCOPYFP(bhi,bt); blo = yh-bhi; \
+  ol = ((ahi*bhi-oh)+ahi*blo+alo*bhi)+alo*blo; \
+}
+
+#define rrddtoS64(ob,xh,xl) \
+{ \
+  double ahi=0,alo,vh,t;\
+  ob = (rS64)ph;\
+  vh=(double)ob;\
+  ahi = ( xh - vh );\
+  t = ( ahi - xh );\
+  alo = (xh-(ahi-t))-(vh+t);\
+  ob += (rS64)(ahi+alo+xl);\
+}
+
+
+#define rrddrenorm(oh,ol) { double s; s=oh+ol; ol=ol-(s-oh); oh=s; }
+
+#define rrddmultlo(oh,ol,xh,xl,yh,yl) \
+  ol = ol + ( xh*yl + xl*yh ); \
+
+#define rrddmultlos(oh,ol,xh,yl) \
+  ol = ol + ( xh*yl ); \
+
+static void rrraise_to_power10( double *ohi, double *olo, double d, rS32 power )  // power can be -323 to +350
+{
+  double ph, pl;
+  if ((power>=0) && (power<=22))
+  {
+    rrddmulthi(ph,pl,d,rrbot[power]);
+  }
+  else
+  {
+    rS32 e,et,eb;
+    double p2h,p2l;
+
+    e=power; if (power<0) e=-e; 
+    et = (e*0x2c9)>>14;/* %23 */ if (et>13) et=13; eb = e-(et*23);
+
+    ph = d; pl = 0.0;
+    if (power<0)
+    {
+      if (eb) { --eb; rrddmulthi(ph,pl,d,rrnegbot[eb]); rrddmultlos(ph,pl,d,rrnegboterr[eb]); }
+      if (et)
+      { 
+        rrddrenorm(ph,pl);
+        --et; rrddmulthi(p2h,p2l,ph,rrnegtop[et]); rrddmultlo(p2h,p2l,ph,pl,rrnegtop[et],rrnegtoperr[et]); ph=p2h;pl=p2l;
+      }
+    }
+    else
+    {
+      if (eb) 
+      { 
+        e = eb; if (eb>22) eb=22; e -= eb;
+        rrddmulthi(ph,pl,d,rrbot[eb]); 
+        if ( e ) { rrddrenorm(ph,pl); rrddmulthi(p2h,p2l,ph,rrbot[e]); rrddmultlos(p2h,p2l,rrbot[e],pl); ph=p2h;pl=p2l; }
+      }
+      if (et)
+      {
+        rrddrenorm(ph,pl);
+        --et; rrddmulthi(p2h,p2l,ph,rrtop[et]); rrddmultlo(p2h,p2l,ph,pl,rrtop[et],rrtoperr[et]); ph=p2h;pl=p2l;
+      }
+    }
+  }
+  rrddrenorm(ph,pl);
+  *ohi = ph; *olo = pl;
+}
+
+// given a float value, returns the significant bits in bits, and the position of the
+//   decimal point in decimal_pos.  +/-INF and NAN are specified by special values
+//   returned in the decimal_pos parameter.
+// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
+static rS32 rrreal_to_str( char const * * start, rU32 * len, char *out, rS32 * decimal_pos, double value, rU32 frac_digits )
+{
+  double d;
+  rS64 bits = 0;
+  rS32 expo, e, ng, tens;
+
+  d = value;
+  RRCOPYFP(bits,d);
+  expo = (bits >> 52) & 2047;
+  ng = (rS32)(bits >> 63);
+  if (ng) d=-d;
+
+  if ( expo == 2047 ) // is nan or inf?
+  {
+    *start = (bits&((((rU64)1)<<52)-1)) ? "NaN" : "Inf";
+    *decimal_pos =  RRSPECIAL;
+    *len = 3;
+    return ng;
+  } 
+
+  if ( expo == 0 ) // is zero or denormal
+  {
+    if ((bits<<1)==0) // do zero
+    {
+      *decimal_pos = 1; 
+      *start = out;
+      out[0] = '0'; *len = 1;
+      return ng;
+    }
+    // find the right expo for denormals
+    {
+      rS64 v = ((rU64)1)<<51;
+      while ((bits&v)==0) { --expo; v >>= 1; }
+    }
+  }
+
+  // find the decimal exponent as well as the decimal bits of the value
+  {
+    double ph,pl;
+
+    // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
+    tens=expo-1023; tens = (tens<0)?((tens*617)/2048):(((tens*1233)/4096)+1);
+
+    // move the significant bits into position and stick them into an int 
+    rrraise_to_power10( &ph, &pl, d, 18-tens );
+
+    // get full as much precision from double-double as possible
+    rrddtoS64( bits, ph,pl );
+
+    // check if we undershot
+    if ( ((rU64)bits) >= rrtento19th ) ++tens; 
+  }
+
+  // now do the rounding in integer land
+  frac_digits = ( frac_digits & 0x80000000 ) ? ( (frac_digits&0x7ffffff) + 1 ) : ( tens + frac_digits );
+  if ( ( frac_digits < 24 ) )
+  {
+    rU32 dg = 1; if ((rU64)bits >= rrpot[9] ) dg=10; while( (rU64)bits >= rrpot[dg] ) { ++dg; if (dg==20) goto noround; }
+    if ( frac_digits < dg )
+    {
+      rU64 r;
+      // add 0.5 at the right position and round
+      e = dg - frac_digits;
+      if ( (rU32)e >= 24 ) goto noround;
+      r = rrpot[e];
+      bits = bits + (r/2);
+      if ( (rU64)bits >= rrpot[dg] ) ++tens;
+      bits /= r;
+    } 
+    noround:;
+  }
+
+  // kill long trailing runs of zeros
+  if ( bits )
+  {
+    rU32 n; for(;;) { if ( bits<=0xffffffff ) break; if (bits%1000) goto donez; bits/=1000; } n = (rU32)bits; while ((n%1000)==0) n/=1000; bits=n; donez:;
+  }
+
+  // convert to string
+  out += 64;
+  e = 0; 
+  for(;;)
+  {
+    rU32 n;
+    char * o = out-8;
+    // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
+    if (bits>=100000000) { n = (rU32)( bits % 100000000);  bits /= 100000000; } else {n = (rU32)bits; bits = 0; }
+    while(n) { out-=2; *(rU16*)out=*(rU16*)&rrdiglookup[(n%100)*2]; n/=100; e+=2; }
+    if (bits==0) { if ((e) && (out[0]=='0')) { ++out; --e; } break; }
+    while( out!=o ) { *--out ='0'; ++e; }
+  }
+  
+  *decimal_pos = tens;
+  *start = out;
+  *len = e;
+  return ng;
+}
+
+#undef rrddmulthi
+#undef rrddrenorm
+#undef rrddmultlo
+#undef rrddmultlos
+#undef RRSPECIAL 
+#undef RRCOPYFP
+ 
+#endif
+
+// clean up
+#undef rU16
+#undef rU32 
+#undef rS32 
+#undef rU64
+#undef rS64
+#undef RRPUBLIC_DEC
+#undef RRPUBLIC_DEF
+#undef RR_SPRINTF_DECORATE
+#undef RR_UNALIGNED
+
+#endif
+
+#endif

+ 1 - 0
docs/other_libs.md

@@ -0,0 +1 @@
+Moved to https://github.com/nothings/single_file_libs

+ 3 - 3
docs/stb_howto.txt

@@ -165,9 +165,9 @@ public domain declarations aren't necessary recognized
 in the USA and some other locations. For that reason,
 I recommend a declaration along these lines:
 
-// This software is in the public domain. Where that dedication is not
-// recognized, you are granted a perpetual, irrevocable license to copy
-// and modify this file as you see fit.
+// This software is dual-licensed to the public domain and under the following
+// license: you are granted a perpetual, irrevocable license to copy, modify,
+// publish, and distribute this file as you see fit.
 
 I typically place this declaration at the end of the initial
 comment block of the file and just say 'public domain'

+ 3 - 3
docs/why_public_domain.md

@@ -95,9 +95,9 @@ public domain declarations aren't necessary recognized
 in the USA and some other locations. For that reason,
 I recommend a declaration along these lines:
 
-// This software is in the public domain. Where that dedication is not
-// recognized, you are granted a perpetual, irrevocable license to copy
-// and modify this file as you see fit.
+// This software is dual-licensed to the public domain and under the following
+// license: you are granted a perpetual, irrevocable license to copy, modify,
+// publish, and distribute this file as you see fit.
 
 I typically place this declaration at the end of the initial
 comment block of the file and just say 'public domain'

+ 313 - 75
stb.h

@@ -1,4 +1,4 @@
-/* stb.h - v2.24 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h
+/* stb.h - v2.29 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h
           no warranty is offered or implied; use this code at your own risk
 
    This is a single header file with a bunch of useful utilities
@@ -25,6 +25,11 @@
 
 Version History
 
+   2.29   attempt to fix use of swprintf()
+   2.28   various new functionality
+   2.27   test _WIN32 not WIN32 in STB_THREADS
+   2.26   various warning & bugfixes
+   2.25   various warning & bugfixes
    2.24   various warning & bugfixes
    2.23   fix 2.22
    2.22   64-bit fixes from '!='; fix stb_sdict_copy() to have preferred name
@@ -166,6 +171,26 @@ Version History
           (stb_array), (stb_arena)
 
 Parenthesized items have since been removed.
+
+LICENSE
+
+ See end of file for license information.
+
+CREDITS
+
+ Written by Sean Barrett.
+
+ Fixes:
+  Philipp Wiesemann
+  Robert Nix
+  r-lyeh
+  blackpawn
+  Mojofreem@github
+  Ryan Whitworth
+  Vincent Isambart
+  Mike Sartain
+  Eugene Opalev
+  Tim Sjostrand
 */
 
 #ifndef STB__INCLUDE_STB_H
@@ -186,10 +211,28 @@ Parenthesized items have since been removed.
    #endif
 #endif
 
+#if defined(_WIN32) && !defined(__MINGW32__)
+   #ifndef _CRT_SECURE_NO_WARNINGS
+   #define _CRT_SECURE_NO_WARNINGS
+   #endif
+   #ifndef _CRT_NONSTDC_NO_DEPRECATE
+   #define _CRT_NONSTDC_NO_DEPRECATE
+   #endif
+   #ifndef _CRT_NON_CONFORMING_SWPRINTFS
+   #define _CRT_NON_CONFORMING_SWPRINTFS
+   #endif
+   #if !defined(_MSC_VER) || _MSC_VER > 1700
+   #include <intrin.h> // _BitScanReverse
+   #endif
+#endif
+
 #include <stdlib.h>     // stdlib could have min/max
 #include <stdio.h>      // need FILE
 #include <string.h>     // stb_define_hash needs memcpy/memset
 #include <time.h>       // stb_dirtree
+#ifdef __MINGW32__
+   #include <fcntl.h>   // O_RDWR
+#endif
 
 #ifdef STB_PERSONAL
    typedef int Bool;
@@ -323,7 +366,7 @@ typedef char stb__testsize2_64[sizeof(stb_uint64)==8 ? 1 : -1];
 
 // add platform-specific ways of checking for sizeof(char*) == 8,
 // and make those define STB_PTR64
-#if defined(_WIN64) || defined(__x86_64__) || defined(__ia64__)
+#if defined(_WIN64) || defined(__x86_64__) || defined(__ia64__) || defined(__LP64__)
   #define STB_PTR64
 #endif
 
@@ -682,20 +725,42 @@ STB_EXTERN char * stb_sstrdup(char *s);
 STB_EXTERN void stbprint(const char *fmt, ...);
 STB_EXTERN char *stb_sprintf(const char *fmt, ...);
 STB_EXTERN char *stb_mprintf(const char *fmt, ...);
+STB_EXTERN int  stb_snprintf(char *s, size_t n, const char *fmt, ...);
+STB_EXTERN int  stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v);
 
 #ifdef STB_DEFINE
+
+int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v)
+{
+   int res;
+   #ifdef _WIN32
+   // Could use "_vsnprintf_s(s, n, _TRUNCATE, fmt, v)" ?
+   res = _vsnprintf(s,n,fmt,v);
+   #else
+   res = vsnprintf(s,n,fmt,v);
+   #endif
+   if (n) s[n-1] = 0;
+   // Unix returns length output would require, Windows returns negative when truncated.
+   return (res >= (int) n || res < 0) ? -1 : res;
+}
+
+int stb_snprintf(char *s, size_t n, const char *fmt, ...)
+{
+   int res;
+   va_list v;
+   va_start(v,fmt);
+   res = stb_vsnprintf(s, n, fmt, v);
+   va_end(v);
+   return res;
+}
+
 char *stb_sprintf(const char *fmt, ...)
 {
    static char buffer[1024];
    va_list v;
    va_start(v,fmt);
-   #ifdef _WIN32
-   _vsnprintf(buffer, 1024, fmt, v);
-   #else
-   vsnprintf(buffer, 1024, fmt, v);
-   #endif
+   stb_vsnprintf(buffer,1024,fmt,v);
    va_end(v);
-   buffer[1023] = 0;
    return buffer;
 }
 
@@ -704,13 +769,8 @@ char *stb_mprintf(const char *fmt, ...)
    static char buffer[1024];
    va_list v;
    va_start(v,fmt);
-   #ifdef _WIN32
-   _vsnprintf(buffer, 1024, fmt, v);
-   #else
-   vsnprintf(buffer, 1024, fmt, v);
-   #endif
+   stb_vsnprintf(buffer,1024,fmt,v);
    va_end(v);
-   buffer[1023] = 0;
    return strdup(buffer);
 }
 
@@ -810,9 +870,8 @@ void stbprint(const char *fmt, ...)
    va_list v;
 
    va_start(v,fmt);
-   res = _vsnprintf(buffer, sizeof(buffer), fmt, v);
+   res = stb_vsnprintf(buffer, sizeof(buffer), fmt, v);
    va_end(v);
-   buffer[sizeof(buffer)-1] = 0;
 
    if (res < 0) {
       tbuf = (char *) malloc(16384);
@@ -1021,9 +1080,15 @@ void stb_fatal(char *s, ...)
    vfprintf(stderr, s, a);
    va_end(a);
    fputs("\n", stderr);
-   #ifdef _WIN32
    #ifdef STB_DEBUG
+   #ifdef _MSC_VER
+   #ifndef STB_PTR64
    __asm int 3;   // trap to debugger!
+   #else
+   __debugbreak();
+   #endif
+   #else
+   __builtin_trap();
    #endif
    #endif
    exit(1);
@@ -1395,13 +1460,13 @@ int stb_is_pow2(unsigned int n)
 
 // tricky use of 4-bit table to identify 5 bit positions (note the '-1')
 // 3-bit table would require another tree level; 5-bit table wouldn't save one
-#ifdef _WIN32
+#if defined(_WIN32) && !defined(__MINGW32__)
 #pragma warning(push)
 #pragma warning(disable: 4035)  // disable warning about no return value
 int stb_log2_floor(unsigned int n)
 {
    #if _MSC_VER > 1700
-   DWORD i;
+   unsigned long i;
    _BitScanReverse(&i, n);
    return i != 0 ? i : -1;
    #else
@@ -1727,6 +1792,7 @@ STB_EXTERN char * stb_strichr(char *s, char t);
 STB_EXTERN char * stb_stristr(char *s, char *t);
 STB_EXTERN int    stb_prefix_count(char *s, char *t);
 STB_EXTERN char * stb_plural(int n);  // "s" or ""
+STB_EXTERN size_t stb_strscpy(char *d, const char *s, size_t n);
 
 STB_EXTERN char **stb_tokens(char *src, char *delimit, int *count);
 STB_EXTERN char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out);
@@ -1741,6 +1807,17 @@ STB_EXTERN char **stb_tokens_quoted(char *src, char *delimit, int *count);
 
 #ifdef STB_DEFINE
 
+size_t stb_strscpy(char *d, const char *s, size_t n)
+{
+   size_t len = strlen(s);
+   if (len >= n) {
+      if (n) d[0] = 0;
+      return 0;
+   }
+   strcpy(d,s);
+   return len + 1;
+}
+
 char *stb_plural(int n)
 {
    return n == 1 ? "" : "s";
@@ -2561,7 +2638,8 @@ void stb_malloc_validate(void *p, void *parent)
 static void * stb__try_chunk(stb__chunk *c, int size, int align, int pre_align)
 {
    char *memblock = (char *) (c+1), *q;
-   int  iq, start_offset;
+   stb_inta iq;
+   int start_offset;
 
    // we going to allocate at the end of the chunk, not the start. confusing,
    // but it means we don't need both a 'limit' and a 'cur', just a 'cur'.
@@ -2973,8 +3051,8 @@ typedef struct
 #define stb_arr_check(a)       assert(!a || stb_arrhead(a)->signature == stb_arr_signature)
 #define stb_arr_check2(a)      assert(!a || stb_arrhead2(a)->signature == stb_arr_signature)
 #else
-#define stb_arr_check(a)       0
-#define stb_arr_check2(a)      0
+#define stb_arr_check(a)       ((void) 0)
+#define stb_arr_check2(a)      ((void) 0)
 #endif
 
 // ARRAY LENGTH
@@ -3023,7 +3101,7 @@ typedef struct
 #define stb_arr_insertn(a,i,n) (stb__arr_insertn((void **) &(a), sizeof(*a), i, n))
 
 // insert an element at i
-#define stb_arr_insert(a,i,v)  (stb__arr_insertn((void **) &(a), sizeof(*a), i, n), ((a)[i] = v))
+#define stb_arr_insert(a,i,v)  (stb__arr_insertn((void **) &(a), sizeof(*a), i, 1), ((a)[i] = v))
 
 // delete N elements from the middle starting at index 'i'
 #define stb_arr_deleten(a,i,n) (stb__arr_deleten((void **) &(a), sizeof(*a), i, n))
@@ -3209,7 +3287,7 @@ void stb__arr_insertn_(void **pp, int size, int i, int n  STB__PARAMS)
       }
 
       z = stb_arr_len2(p);
-      stb__arr_addlen_(&p, size, i  STB__ARGS);
+      stb__arr_addlen_(&p, size, n  STB__ARGS);
       memmove((char *) p + (i+n)*size, (char *) p + i*size, size * (z-i));
    }
    *pp = p;
@@ -3219,7 +3297,7 @@ void stb__arr_deleten_(void **pp, int size, int i, int n  STB__PARAMS)
 {
    void *p = *pp;
    if (n) {
-      memmove((char *) p + i*size, (char *) p + (i+n)*size, size * (stb_arr_len2(p)-i));
+      memmove((char *) p + i*size, (char *) p + (i+n)*size, size * (stb_arr_len2(p)-(i+n)));
       stb_arrhead2(p)->len -= n;
    }
    *pp = p;
@@ -3297,7 +3375,7 @@ unsigned int stb_hashptr(void *p)
 
 unsigned int stb_rehash_improved(unsigned int v)
 {
-   return stb_hashptr((void *) v);
+   return stb_hashptr((void *)(size_t) v);
 }
 
 unsigned int stb_hash2(char *str, unsigned int *hash2_ptr)
@@ -5049,7 +5127,7 @@ void stb_fwrite32(FILE *f, stb_uint32 x)
    fwrite(&x, 4, 1, f);
 }
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER) || defined(__MINGW32__)
    #define stb__stat   _stat
 #else
    #define stb__stat   stat
@@ -5112,7 +5190,18 @@ int stb_filewrite(char *filename, void *data, size_t length)
 {
    FILE *f = stb_fopen(filename, "wb");
    if (f) {
-      fwrite(data, 1, length, f);
+      unsigned char *data_ptr = (unsigned char *) data;
+      size_t remaining = length;
+      while (remaining > 0) {
+         size_t len2 = remaining > 65536 ? 65536 : remaining;
+         size_t len3 = fwrite(data_ptr, 1, len2, f);
+         if (len2 != len3) {
+            fprintf(stderr, "Failed while writing %s\n", filename);
+            break;
+         }
+         remaining -= len2;
+         data_ptr += len2;
+      }
       stb_fclose(f, stb_keep_if_different);
    }
    return f != NULL;
@@ -5389,7 +5478,11 @@ FILE *  stb_fopen(char *filename, char *mode)
    #else
    {
       strcpy(temp_full+p, "stmpXXXXXX");
-      int fd = mkstemp(temp_full);
+      #ifdef __MINGW32__
+         int fd = open(mktemp(temp_full), O_RDWR);
+      #else
+         int fd = mkstemp(temp_full);
+      #endif
       if (fd == -1) return NULL;
       f = fdopen(fd, mode);
       if (f == NULL) {
@@ -5708,6 +5801,19 @@ char *stb_strip_final_slash(char *t)
    }
    return t;
 }
+
+char *stb_strip_final_slash_regardless(char *t)
+{
+   if (t[0]) {
+      char *z = t + strlen(t) - 1;
+      // *z is the last character
+      if (*z == '\\' || *z == '/')
+         *z = 0;
+      if (*z == '\\')
+         *z = '/'; // canonicalize to make sure it matches db
+   }
+   return t;
+}
 #endif
 
 //////////////////////////////////////////////////////////////////////////////
@@ -5820,42 +5926,57 @@ void stb_readdir_free(char **files)
    stb_arr_free(f2);
 }
 
+static int isdotdirname(char *name)
+{
+   if (name[0] == '.')
+      return (name[1] == '.') ? !name[2] : !name[1];
+   return 0;
+}
+
 STB_EXTERN int stb_wildmatchi(char *expr, char *candidate);
 static char **readdir_raw(char *dir, int return_subdirs, char *mask)
 {
    char **results = NULL;
-   char buffer[512], with_slash[512];
-   int n;
+   char buffer[4096], with_slash[4096];
+   size_t n;
 
    #ifdef _MSC_VER
       stb__wchar *ws;
       struct _wfinddata_t data;
+   #ifdef _WIN64
+      const intptr_t none = -1;
+      intptr_t z;
+   #else
       const long none = -1;
       long z;
-   #else
+   #endif
+   #else // !_MSC_VER
       const DIR *none = NULL;
       DIR *z;
    #endif
 
-   strcpy(buffer,dir);
+   n = stb_strscpy(buffer,dir,sizeof(buffer));
+   if (!n || n >= sizeof(buffer))
+      return NULL;
    stb_fixpath(buffer);
-   n = strlen(buffer);
+   n--;
 
    if (n > 0 && (buffer[n-1] != '/')) {
       buffer[n++] = '/';
    }
    buffer[n] = 0;
-   strcpy(with_slash, buffer);
+   if (!stb_strscpy(with_slash,buffer,sizeof(with_slash)))
+      return NULL;
 
    #ifdef _MSC_VER
-      strcpy(buffer+n, "*.*");
+      if (!stb_strscpy(buffer+n,"*.*",sizeof(buffer)-n))
+         return NULL;
       ws = stb__from_utf8(buffer);
       z = _wfindfirst((const wchar_t *)ws, &data);
    #else
       z = opendir(dir);
    #endif
 
-
    if (z != none) {
       int nonempty = STB_TRUE;
       #ifndef _MSC_VER
@@ -5876,17 +5997,18 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask)
             is_subdir = !!(data.attrib & _A_SUBDIR);
             #else
             char *name = data->d_name;
-            strcpy(buffer+n,name);
-            DIR *y = opendir(buffer);
-            is_subdir = (y != NULL);
-            if (y != NULL) closedir(y);
+            if (!stb_strscpy(buffer+n,name,sizeof(buffer)-n))
+               break;
+            // Could follow DT_LNK, but would need to check for recursive links.
+            is_subdir = !!(data->d_type & DT_DIR);
             #endif
-        
+
             if (is_subdir == return_subdirs) {
-               if (!is_subdir || name[0] != '.') {
+               if (!is_subdir || !isdotdirname(name)) {
                   if (!mask || stb_wildmatchi(mask, name)) {
-                     char buffer[512],*p=buffer;
-                     sprintf(buffer, "%s%s", with_slash, name);
+                     char buffer[4096],*p=buffer;
+                     if ( stb_snprintf(buffer, sizeof(buffer), "%s%s", with_slash, name) < 0 )
+                        break;
                      if (buffer[0] == '.' && buffer[1] == '/')
                         p = buffer+2;
                      stb_arr_push(results, strdup(p));
@@ -6649,14 +6771,16 @@ typedef struct
    char   * path;           // full path from passed-in root
    time_t   last_modified;
    int      num_files;
+   int      flag;
 } stb_dirtree_dir;
 
 typedef struct
 {
    char *name;              // name relative to path
    int   dir;               // index into dirs[] array
-   unsigned long size;      // size, max 4GB
+   stb_int64 size;      // size, max 4GB
    time_t   last_modified;
+   int      flag;
 } stb_dirtree_file;
 
 typedef struct
@@ -6684,6 +6808,14 @@ extern stb_dirtree *stb_dirtree_get_with_file ( char *dir, char *cache_file);
 // do a call to stb_dirtree_get() with the same cache file at about the same
 // time, but I _think_ it might just work.
 
+// i needed to build an identical data structure representing the state of
+// a mirrored copy WITHOUT bothering to rescan it (i.e. we're mirroring to
+// it WITHOUT scanning it, e.g. it's over the net), so this requires access
+// to all of the innards.
+extern void stb_dirtree_db_add_dir(stb_dirtree *active, char *path, time_t last);
+extern void stb_dirtree_db_add_file(stb_dirtree *active, char *name, int dir, stb_int64 size, time_t last);
+extern void stb_dirtree_db_read(stb_dirtree *target, char *filename, char *dir);
+extern void stb_dirtree_db_write(stb_dirtree *target, char *filename, char *dir);
 
 #ifdef STB_DEFINE
 static void stb__dirtree_add_dir(char *path, time_t last, stb_dirtree *active)
@@ -6695,7 +6827,7 @@ static void stb__dirtree_add_dir(char *path, time_t last, stb_dirtree *active)
    stb_arr_push(active->dirs, d);
 }
 
-static void stb__dirtree_add_file(char *name, int dir, unsigned long size, time_t last, stb_dirtree *active)
+static void stb__dirtree_add_file(char *name, int dir, stb_int64 size, time_t last, stb_dirtree *active)
 {
    stb_dirtree_file f;
    f.dir = dir;
@@ -6706,23 +6838,25 @@ static void stb__dirtree_add_file(char *name, int dir, unsigned long size, time_
    stb_arr_push(active->files, f);
 }
 
-static char stb__signature[12] = { 's', 'T', 'b', 'D', 'i', 'R', 't', 'R', 'e', 'E', '0', '1' };
+// version 02 supports > 4GB files
+static char stb__signature[12] = { 's', 'T', 'b', 'D', 'i', 'R', 't', 'R', 'e', 'E', '0', '2' };
 
 static void stb__dirtree_save_db(char *filename, stb_dirtree *data, char *root)
 {
    int i, num_dirs_final=0, num_files_final;
+   char *info = root ? root : "";
    int *remap;
    FILE *f = fopen(filename, "wb");
    if (!f) return;
 
    fwrite(stb__signature, sizeof(stb__signature), 1, f);
-   fwrite(root, strlen(root)+1, 1, f);
+   fwrite(info, strlen(info)+1, 1, f);
    // need to be slightly tricky and not write out NULLed directories, nor the root
 
    // build remapping table of all dirs we'll be writing out
    remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(data->dirs));
    for (i=0; i < stb_arr_len(data->dirs); ++i) {
-      if (data->dirs[i].path == NULL || 0==stb_stricmp(data->dirs[i].path, root)) {
+      if (data->dirs[i].path == NULL || (root && 0==stb_stricmp(data->dirs[i].path, root))) {
          remap[i] = -1;
       } else {
          remap[i] = num_dirs_final++;
@@ -6739,14 +6873,14 @@ static void stb__dirtree_save_db(char *filename, stb_dirtree *data, char *root)
 
    num_files_final = 0;
    for (i=0; i < stb_arr_len(data->files); ++i)
-      if (remap[data->files[i].dir] >= 0)
+      if (remap[data->files[i].dir] >= 0 && data->files[i].name)
          ++num_files_final;
 
    fwrite(&num_files_final, 4, 1, f);
    for (i=0; i < stb_arr_len(data->files); ++i) {
-      if (remap[data->files[i].dir] >= 0) {
+      if (remap[data->files[i].dir] >= 0 && data->files[i].name) {
          stb_fput_ranged(f, remap[data->files[i].dir], 0, num_dirs_final);
-         stb_fput_varlenu(f, data->files[i].size);
+         stb_fput_varlen64(f, data->files[i].size);
          fwrite(&data->files[i].last_modified, 4, 1, f);
          stb_fput_string(f, data->files[i].name);
       }
@@ -6783,7 +6917,7 @@ static void stb__dirtree_load_db(char *filename, stb_dirtree *data, char *dir)
    stb_arr_setlen(data->files, n);
    for (i=0; i < stb_arr_len(data->files); ++i) {
       data->files[i].dir  = stb_fget_ranged(f, 0, stb_arr_len(data->dirs));
-      data->files[i].size = stb_fget_varlenu(f);
+      data->files[i].size = stb_fget_varlen64(f);
       fread(&data->files[i].last_modified, 4, 1, f);
       data->files[i].name = stb_fget_string(f, data->string_pool);
       if (data->files[i].name == NULL) goto bail;
@@ -6797,6 +6931,7 @@ static void stb__dirtree_load_db(char *filename, stb_dirtree *data, char *dir)
    fclose(f);
 }
 
+static int stb__dircount, stb__dircount_mask, stb__showfile;
 static void stb__dirtree_scandir(char *path, time_t last_time, stb_dirtree *active)
 {
    // this is dumb depth first; theoretically it might be faster
@@ -6805,48 +6940,75 @@ static void stb__dirtree_scandir(char *path, time_t last_time, stb_dirtree *acti
 
    int n;
 
-   struct _wfinddata_t c_file;
+   struct _wfinddatai64_t c_file;
    long hFile;
    stb__wchar full_path[1024];
    int has_slash;
+   if (stb__showfile) printf("<");
 
    has_slash = (path[0] && path[strlen(path)-1] == '/'); 
+
+   // @TODO: do this concatenation without using swprintf to avoid this mess:
+#if defined(_MSC_VER) && _MSC_VER < 1400
+   if (has_slash)
+      swprintf(full_path, L"%s*", stb__from_utf8(path));
+   else
+      swprintf(full_path, L"%s/*", stb__from_utf8(path));
+#else
    if (has_slash)
-      swprintf((wchar_t *)full_path, L"%s*", stb__from_utf8(path));
+      swprintf(full_path, 1024, L"%s*", stb__from_utf8(path));
    else
-      swprintf((wchar_t *)full_path, L"%s/*", stb__from_utf8(path));
+      swprintf(full_path, 1024, L"%s/*", stb__from_utf8(path));
+#endif
 
    // it's possible this directory is already present: that means it was in the
    // cache, but its parent wasn't... in that case, we're done with it
+   if (stb__showfile) printf("C[%d]", stb_arr_len(active->dirs));
    for (n=0; n < stb_arr_len(active->dirs); ++n)
-      if (0 == stb_stricmp(active->dirs[n].path, path))
+      if (0 == stb_stricmp(active->dirs[n].path, path)) {
+         if (stb__showfile) printf("D");
          return;
+      }
+   if (stb__showfile) printf("E");
 
    // otherwise, we need to add it
    stb__dirtree_add_dir(path, last_time, active);
    n = stb_arr_lastn(active->dirs);
 
-   if( (hFile = _wfindfirst((const wchar_t *)full_path, &c_file )) != -1L ) {
+   if (stb__showfile) printf("[");
+   if( (hFile = _wfindfirsti64( full_path, &c_file )) != -1L ) {
       do {
+         if (stb__showfile) printf(")");
          if (c_file.attrib & _A_SUBDIR) {
             // ignore subdirectories starting with '.', e.g. "." and ".."
             if (c_file.name[0] != '.') {
                char *new_path = (char *) full_path;
-               char *temp = stb__to_utf8((stb__wchar *)c_file.name);
+               char *temp = stb__to_utf8(c_file.name);
+
                if (has_slash)
                   sprintf(new_path, "%s%s", path, temp);
                else
                   sprintf(new_path, "%s/%s", path, temp);
+
+               if (stb__dircount_mask) {
+                  ++stb__dircount;
+                  if (!(stb__dircount & stb__dircount_mask)) {
+                     printf("%s\r", new_path);
+                  }
+               }
+
                stb__dirtree_scandir(new_path, c_file.time_write, active);
             }
          } else {
-            char *temp = stb__to_utf8((stb__wchar *)c_file.name);
+            char *temp = stb__to_utf8(c_file.name);
             stb__dirtree_add_file(temp, n, c_file.size, c_file.time_write, active);
          }
-      } while( _wfindnext( hFile, &c_file ) == 0 );
-
+         if (stb__showfile) printf("(");
+      } while( _wfindnexti64( hFile, &c_file ) == 0 );
+      if (stb__showfile) printf("]");
       _findclose( hFile );
    }
+   if (stb__showfile) printf(">\n");
 }
 
 // scan the database and see if it's all valid
@@ -6862,13 +7024,21 @@ static int stb__dirtree_update_db(stb_dirtree *db, stb_dirtree *active)
 
    for (i=0; i < stb_arr_len(db->dirs); ++i) {
       struct _stat info;
+      if (stb__dircount_mask) {
+         ++stb__dircount;
+         if (!(stb__dircount & stb__dircount_mask)) {
+            printf(".");
+         }
+      }
       if (0 == _stat(db->dirs[i].path, &info)) {
          if (info.st_mode & _S_IFDIR) {
             // it's still a directory, as expected
-            if (info.st_mtime > db->dirs[i].last_modified) {
+            int n = abs(info.st_mtime - db->dirs[i].last_modified);
+            if (n > 1 && n != 3600) {  // the 3600 is a hack because sometimes this jumps for no apparent reason, even when no time zone or DST issues are at play
                // it's changed! force a rescan
                // we don't want to scan it until we've stat()d its
                // subdirs, though, so we queue it
+               if (stb__showfile) printf("Changed: %s - %08x:%08x\n", db->dirs[i].path, db->dirs[i].last_modified, info.st_mtime);
                stb_arr_push(rescan, i);
                // update the last_mod time
                db->dirs[i].last_modified = info.st_mtime;
@@ -6946,6 +7116,8 @@ stb_dirtree *stb_dirtree_get_with_file(char *dir, char *cache_file)
 
    if (cache_file != NULL)
       stb__dirtree_load_db(cache_file, &db, stripped_dir);
+   else if (stb__showfile)
+      printf("No cache file\n");
 
    active.files = NULL;
    active.dirs = NULL;
@@ -6960,6 +7132,9 @@ stb_dirtree *stb_dirtree_get_with_file(char *dir, char *cache_file)
 
    stb__dirtree_scandir(stripped_dir, 0, &active);  // no last_modified time available for root
 
+   if (stb__dircount_mask)
+      printf("                                                                              \r");
+
    // done with the DB; write it back out if any changes, i.e. either
    //      1. any inconsistency found between cached information and actual disk
    //   or 2. if scanning the root found any new directories--which we detect because
@@ -7022,6 +7197,32 @@ void stb_dirtree_free(stb_dirtree *d)
    stb__dirtree_free_raw(d);
    free(d);
 }
+
+void stb_dirtree_db_add_dir(stb_dirtree *active, char *path, time_t last)
+{
+   stb__dirtree_add_dir(path, last, active);
+}
+
+void stb_dirtree_db_add_file(stb_dirtree *active, char *name, int dir, stb_int64 size, time_t last)
+{
+   stb__dirtree_add_file(name, dir, size, last, active);
+}
+
+void stb_dirtree_db_read(stb_dirtree *target, char *filename, char *dir)
+{
+   char *s = stb_strip_final_slash(strdup(dir));
+   target->dirs = 0;
+   target->files = 0;
+   target->string_pool = 0;
+   stb__dirtree_load_db(filename, target, s);
+   free(s);
+}
+
+void stb_dirtree_db_write(stb_dirtree *target, char *filename, char *dir)
+{
+   stb__dirtree_save_db(filename, target, 0); // don't strip out any directories
+}
+
 #endif // STB_DEFINE
 
 #endif // _WIN32
@@ -7338,7 +7539,7 @@ STB_EXTERN void ** stb_ps_fastlist(stb_ps *ps, int *count);
 //     but some entries of the list may be invalid;
 //     test with 'stb_ps_fastlist_valid(x)'
 
-#define stb_ps_fastlist_valid(x)   ((unsigned int) (x) > 1)
+#define stb_ps_fastlist_valid(x)   ((stb_uinta) (x) > 1)
 
 #ifdef STB_DEFINE
 
@@ -7359,8 +7560,6 @@ typedef struct
 #define GetBucket(p)    ((stb_ps_bucket *) ((char *) (p) - STB_ps_bucket))
 #define EncodeBucket(p) ((stb_ps *) ((char *) (p) + STB_ps_bucket))
 
-typedef char stb__verify_bucket_heap_size[sizeof(stb_ps_bucket) == 16];
-
 static void stb_bucket_free(stb_ps_bucket *b)
 {
    free(b);
@@ -7736,7 +7935,7 @@ stb_ps *stb_ps_remove_any(stb_ps *ps, void **value)
 void ** stb_ps_getlist(stb_ps *ps, int *count)
 {
    int i,n=0;
-   void **p;
+   void **p = NULL;
    switch (3 & (int) ps) {
       case STB_ps_direct:
          if (ps == NULL) { *count = 0; return NULL; }
@@ -10058,7 +10257,7 @@ char *stb_decompress_fromfile(char *filename, unsigned int *len)
    if (p == NULL) return NULL;
    if (p[0] != 0x57 || p[1] != 0xBc || p[2] || p[3]) { free(p); return NULL; }
    q = (char *) malloc(stb_decompress_length(p)+1);
-   if (!q) { free(p); free(p); return NULL; }
+   if (!q) { free(p); return NULL; }
    *len = stb_decompress((unsigned char *) q, p, n);
    if (*len) q[*len] = 0;
    free(p);
@@ -10407,15 +10606,15 @@ int stb_compress_intofile(FILE *f, char *input, unsigned int length)
 //////////////////////    streaming I/O version    /////////////////////
 
 
-static stb_uint stb_out_backpatch_id(void)
+static size_t stb_out_backpatch_id(void)
 {
    if (stb__out)
-      return (stb_uint) stb__out;
+      return (size_t) stb__out;
    else
       return ftell(stb__outfile);
 }
 
-static void stb_out_backpatch(stb_uint id, stb_uint value)
+static void stb_out_backpatch(size_t id, stb_uint value)
 {
    stb_uchar data[4] = { value >> 24, value >> 16, value >> 8, value };
    if (stb__out) {
@@ -11107,7 +11306,7 @@ int  stb_arith_decode_byte(stb_arith *a)
 //                         Threads
 //
 
-#ifndef WIN32
+#ifndef _WIN32
 #ifdef STB_THREADS
 #error "threads not implemented except for Windows"
 #endif
@@ -14077,10 +14276,49 @@ void stua_run_script(char *s)
    stua_gc(1);
 }
 #endif // STB_DEFINE
-
 #endif // STB_STUA
 
-
 #undef STB_EXTERN
 #endif // STB_INCLUDE_STB_H
 
+/*
+------------------------------------------------------------------------------
+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.
+------------------------------------------------------------------------------
+*/

+ 201 - 48
stb_c_lexer.h

@@ -1,4 +1,4 @@
-// stb_c_lexer.h - v0.06 - public domain Sean Barrett 2013
+// stb_c_lexer.h - v0.09 - public domain Sean Barrett 2013
 // lexer for making little C-like languages with recursive-descent parsers
 //
 // This file provides both the interface and the implementation.
@@ -10,6 +10,9 @@
 // suffixes on integer constants are not handled (you can override this).
 //
 // History:
+//     0.09 hex floats, no-stdlib fixes
+//     0.08 fix bad pointer comparison
+//     0.07 fix mishandling of hexadecimal constants parsed by strtol
 //     0.06 fix missing next character after ending quote mark (Andreas Fredriksson)
 //     0.05 refixed get_location because github version had lost the fix
 //     0.04 fix octal parsing bug
@@ -28,6 +31,14 @@
 //     - haven't implemented octal/hex character constants
 //     - haven't implemented support for unicode CLEX_char
 //     - need to expand error reporting so you don't just get "CLEX_parse_error"
+//
+// Contributors:
+//   Arpad Goretity (bugfix)
+//   Alan Hickman (hex floats)
+//
+// LICENSE
+//
+//   See end of file for license information.
 
 #ifndef STB_C_LEXER_DEFINITIONS
 // to change the default parsing rules, copy the following lines
@@ -38,7 +49,8 @@
 #define STB_C_LEX_C_DECIMAL_INTS    Y   //  "0|[1-9][0-9]*"                        CLEX_intlit
 #define STB_C_LEX_C_HEX_INTS        Y   //  "0x[0-9a-fA-F]+"                       CLEX_intlit
 #define STB_C_LEX_C_OCTAL_INTS      Y   //  "[0-7]+"                               CLEX_intlit
-#define STB_C_LEX_C_DECIMAL_FLOATS  Y   //  "[0-9]*(.[0-9]*([eE]-?[0-9]+)?)        CLEX_floatlit
+#define STB_C_LEX_C_DECIMAL_FLOATS  Y   //  "[0-9]*(.[0-9]*([eE][-+]?[0-9]+)?)     CLEX_floatlit
+#define STB_C_LEX_C99_HEX_FLOATS    N   //  "0x{hex}+(.{hex}*)?[pP][-+]?{hex}+     CLEX_floatlit
 #define STB_C_LEX_C_IDENTIFIERS     Y   //  "[_a-zA-Z][_a-zA-Z0-9]*"               CLEX_id
 #define STB_C_LEX_C_DQ_STRINGS      Y   //  double-quote-delimited strings with escapes  CLEX_dqstring
 #define STB_C_LEX_C_SQ_STRINGS      N   //  single-quote-delimited strings with escapes  CLEX_ssstring
@@ -77,7 +89,7 @@
 #define STB_C_LEX_DISCARD_PREPROCESSOR    Y   // discard C-preprocessor directives (e.g. after prepocess
                                               // still have #line, #pragma, etc)
 
-//#define STB_C_LEX_ISWHITE(str)    ... // return length in bytes of first character if it is whitespace
+//#define STB_C_LEX_ISWHITE(str)    ... // return length in bytes of whitespace characters if first char is whitespace
 
 #define STB_C_LEXER_DEFINITIONS         // This line prevents the header file from replacing your definitions
 // --END--
@@ -164,11 +176,6 @@ extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where,
 #define Y(x) 1
 #define N(x) 0
 
-#if STB_C_LEX_USE_STDLIB(x)
-#define STB__CLEX_use_stdlib
-#include <stdlib.h>
-#endif
-
 #if STB_C_LEX_INTEGERS_AS_DOUBLES(x)
 typedef double     stb__clex_int;
 #define intfield   real_number
@@ -193,6 +200,10 @@ typedef long       stb__clex_int;
 #define STB__clex_define_shifts
 #endif
 
+#if STB_C_LEX_C99_HEX_FLOATS(x)
+#define STB__clex_hex_floats
+#endif
+
 #if STB_C_LEX_C_HEX_INTS(x)
 #define STB__clex_hex_ints
 #endif
@@ -213,6 +224,11 @@ typedef long       stb__clex_int;
 #define STB__clex_discard_preprocessor
 #endif
 
+#if STB_C_LEX_USE_STDLIB(x) && (!defined(STB__clex_hex_floats) || __STDC_VERSION__ >= 199901L)
+#define STB__CLEX_use_stdlib
+#include <stdlib.h>
+#endif
+
 // Now pick a definition of Y/N that's conducive to
 // defining the enum of token names.
 #if STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) || defined(STB_C_LEXER_SELF_TEST)
@@ -357,34 +373,95 @@ static int stb__clex_parse_suffixes(stb_lexer *lexer, long tokenid, char *start,
 }
 
 #ifndef STB__CLEX_use_stdlib
+static double stb__clex_pow(double base, unsigned int exponent)
+{
+   double value=1;
+   for ( ; exponent; exponent >>= 1) {
+      if (exponent & 1)
+         value *= base;
+      base *= base;
+   }
+   return value;
+}
+
 static double stb__clex_parse_float(char *p, char **q)
 {
+   char *s = p;
    double value=0;
-   while (*p >= '0' && *p <= '9')
-      value = value*10 + (*p++ - '0');
+   int base=10;
+   int exponent=0;
+
+#ifdef STB__clex_hex_floats
+   if (*p == '0') {
+      if (p[1] == 'x' || p[1] == 'X') {
+         base=16;
+         p += 2;
+      }
+   }
+#endif
+
+   for (;;) {
+      if (*p >= '0' && *p <= '9')
+         value = value*base + (*p++ - '0');
+#ifdef STB__clex_hex_floats
+      else if (base == 16 && *p >= 'a' && *p <= 'f')
+         value = value*base + 10 + (*p++ - 'a');
+      else if (base == 16 && *p >= 'A' && *p <= 'F')
+         value = value*base + 10 + (*p++ - 'A');
+#endif
+      else
+         break;
+   }
+
    if (*p == '.') {
-      double powten=1, addend = 0;
+      double pow, addend = 0;
       ++p;
-      while (*p >= '0' && *p <= '9') {
-         addend = addend + 10*(*p++ - '0');
-         powten *= 10;
+      for (pow=1; ; pow*=base) {
+         if (*p >= '0' && *p <= '9')
+            addend = addend*base + (*p++ - '0');
+#ifdef STB__clex_hex_floats
+         else if (base == 16 && *p >= 'a' && *p <= 'f')
+            addend = addend*base + 10 + (*p++ - 'a');
+         else if (base == 16 && *p >= 'A' && *p <= 'F')
+            addend = addend*base + 10 + (*p++ - 'A');
+#endif
+         else
+            break;
       }
-      value += addend / powten;
+      value += addend / pow;
    }
-   if (*p == 'e' || *p == 'E') {
+#ifdef STB__clex_hex_floats
+   if (base == 16) {
+      // exponent required for hex float literal
+      if (*p != 'p' && *p != 'P') {
+         *q = s;
+         return 0;
+      }
+      exponent = 1;
+   } else
+#endif
+      exponent = (*p == 'e' || *p == 'E');
+
+   if (exponent) {
       int sign = p[1] == '-';
-      int exponent=0;
-      double pow10=1;
-      p += 1+sign;
+      unsigned int exponent=0;
+      double power=1;
+      ++p;
+      if (*p == '-' || *p == '+')
+         ++p;
       while (*p >= '0' && *p <= '9')
          exponent = exponent*10 + (*p++ - '0');
-      // can't use pow() from stdlib, so do it slow way
-      while (exponent-- > 0)
-         pow10 *= 10;
+
+#ifdef STB__clex_hex_floats
+      if (base == 16)
+         power = stb__clex_pow(2, exponent);
+      else
+#endif
+         power = stb__clex_pow(10, exponent);
       if (sign)
-         value /= pow10;
+         value /= power;
       else
-         value *= pow10;
+         value *= power;
    }
    *q = p;
    return value;
@@ -452,7 +529,7 @@ int stb_c_lexer_get_token(stb_lexer *lexer)
          int n;
          n = STB_C_LEX_ISWHITE(p);
          if (n == 0) break;
-         if (lexer->eof && lexer+n > lexer->eof)
+         if (lexer->eof && lexer->eof - lexer->parse_point < n)
             return stb__clex_token(tok, CLEX_parse_error, p,lexer->eof-1);
          p += n;
       }
@@ -623,33 +700,57 @@ int stb_c_lexer_get_token(stb_lexer *lexer)
          goto single_char;
 
       case '0':
-         #ifdef STB__clex_hex_ints
+         #if defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats)
             if (p+1 != lexer->eof) {
                if (p[1] == 'x' || p[1] == 'X') {
-                  char *q = p+2;
+                  char *q;
+
+                  #ifdef STB__clex_hex_floats
+                  for (q=p+2;
+                       q != lexer->eof && ((*q >= '0' && *q <= '9') || (*q >= 'a' && *q <= 'f') || (*q >= 'A' && *q <= 'F'));
+                       ++q);
+                  if (q != lexer->eof) {
+                     if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'p' || *q == 'P')) {
+                        #ifdef STB__CLEX_use_stdlib
+                        lexer->real_number = strtod((char *) p, (char**) &q);
+                        #else
+                        lexer->real_number = stb__clex_parse_float(p, &q);
+                        #endif
+
+                        if (p == q)
+                           return stb__clex_token(lexer, CLEX_parse_error, p,q);
+                        return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES);
+
+                     }
+                  }
+                  #endif   // STB__CLEX_hex_floats
+
+                  #ifdef STB__clex_hex_ints
                   #ifdef STB__CLEX_use_stdlib
-                  lexer->int_number = strtol((char *) p, (char **) q, 16);
+                  lexer->int_number = strtol((char *) p, (char **) &q, 16);
                   #else
-                  stb__clex_int n=0;
-                  while (q != lexer->eof) {
-                     if (*q >= '0' && *q <= '9')
-                        n = n*16 + (*q - '0');
-                     else if (*q >= 'a' && *q <= 'f')
-                        n = n*16 + (*q - 'a') + 10;
-                     else if (*q >= 'A' && *q <= 'F')
-                        n = n*16 + (*q - 'A') + 10;
-                     else
-                        break;
-                     ++q;
+                  {
+                     stb__clex_int n=0;
+                     for (q=p+2; q != lexer->eof; ++q) {
+                        if (*q >= '0' && *q <= '9')
+                           n = n*16 + (*q - '0');
+                        else if (*q >= 'a' && *q <= 'f')
+                           n = n*16 + (*q - 'a') + 10;
+                        else if (*q >= 'A' && *q <= 'F')
+                           n = n*16 + (*q - 'A') + 10;
+                        else
+                           break;
+                     }
+                     lexer->int_number = n;
                   }
-                  lexer->int_field = n; // int_field is macro that expands to real_number/int_number depending on type of n
                   #endif
                   if (q == p+2)
                      return stb__clex_token(lexer, CLEX_parse_error, p-2,p-1);
                   return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_HEX_SUFFIXES);
+                  #endif
                }
             }
-         #endif // STB__clex_hex_ints
+         #endif // defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats)
          // can't test for octal because we might parse '0.0' as float or as '0' '.' '0',
          // so have to do float first
 
@@ -685,14 +786,14 @@ int stb_c_lexer_get_token(stb_lexer *lexer)
             stb__clex_int n=0;
             while (q != lexer->eof) {
                if (*q >= '0' && *q <= '7')
-                  n = n*8 + (q - '0');
+                  n = n*8 + (*q - '0');
                else
                   break;
                ++q;
             }
             if (q != lexer->eof && (*q == '8' || *q=='9'))
-               return stb__clex_token(tok, CLEX_parse_error, p, q);
-            lexer->int_field = n;
+               return stb__clex_token(lexer, CLEX_parse_error, p, q);
+            lexer->int_number = n;
             #endif
             return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
          }
@@ -707,12 +808,12 @@ int stb_c_lexer_get_token(stb_lexer *lexer)
             stb__clex_int n=0;
             while (q != lexer->eof) {
                if (*q >= '0' && *q <= '9')
-                  n = n*10 + (q - '0');
+                  n = n*10 + (*q - '0');
                else
                   break;
                ++q;
             }
-            lexer->int_field = n;
+            lexer->int_number = n;
             #endif
             return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
          }
@@ -725,6 +826,7 @@ int stb_c_lexer_get_token(stb_lexer *lexer)
 #ifdef STB_C_LEXER_SELF_TEST
 
 #include <stdio.h>
+#include <stdlib.h>
 
 static void print_token(stb_lexer *lexer)
 {
@@ -780,7 +882,15 @@ multiline comments */
 
 void dummy(void)
 {
-   printf("test",1); // https://github.com/nothings/stb/issues/13
+   double some_floats[] = {
+      1.0501, -10.4e12, 5E+10,
+#if 0   // not support in C++ or C-pre-99, so don't try to compile it
+      0x1.0p+24, 0xff.FP-8, 0x1p-23,
+#endif
+      4.
+   };
+
+   printf("test %d",1); // https://github.com/nothings/stb/issues/13
 }
 
 int main(int argc, char **argv)
@@ -791,11 +901,13 @@ int main(int argc, char **argv)
    stb_lexer lex;
    if (len < 0) {
       fprintf(stderr, "Error opening file\n");
+      free(text);
+      fclose(f);
       return 1;
    }
    fclose(f);
 
-   stb_c_lexer_init(&lex, text, text+len, (char *) malloc(1<<16), 1<<16);
+   stb_c_lexer_init(&lex, text, text+len, (char *) malloc(0x10000), 0x10000);
    while (stb_c_lexer_get_token(&lex)) {
       if (lex.token == CLEX_parse_error) {
          printf("\n<<<PARSE ERROR>>>\n");
@@ -807,3 +919,44 @@ int main(int argc, char **argv)
    return 0;
 }
 #endif
+/*
+------------------------------------------------------------------------------
+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.
+------------------------------------------------------------------------------
+*/

+ 1045 - 0
stb_connected_components.h

@@ -0,0 +1,1045 @@
+// stb_connected_components - v0.95 - public domain connected components on grids
+//                                                 http://github.com/nothings/stb
+//
+// Finds connected components on 2D grids for testing reachability between
+// two points, with fast updates when changing reachability (e.g. on one machine
+// it was typically 0.2ms w/ 1024x1024 grid). Each grid square must be "open" or
+// "closed" (traversable or untraversable), and grid squares are only connected
+// to their orthogonal neighbors, not diagonally. 
+//
+// In one source file, create the implementation by doing something like this:
+//
+//   #define STBCC_GRID_COUNT_X_LOG2    10
+//   #define STBCC_GRID_COUNT_Y_LOG2    10
+//   #define STB_CONNECTED_COMPONENTS_IMPLEMENTATION
+//   #include "stb_connected_components.h"
+//
+// The above creates an implementation that can run on maps up to 1024x1024.
+// Map sizes must be a multiple of (1<<(LOG2/2)) on each axis (e.g. 32 if LOG2=10,
+// 16 if LOG2=8, etc.) (You can just pad your map with untraversable space.)
+// 
+// MEMORY USAGE
+//
+//   Uses about 6-7 bytes per grid square (e.g. 7MB for a 1024x1024 grid).
+//   Uses a single worst-case allocation which you pass in.
+//
+// PERFORMANCE
+//
+//   On a core i7-2700K at 3.5 Ghz, for a particular 1024x1024 map (map_03.png):
+//
+//       Creating map                   : 44.85 ms
+//       Making one square   traversable:  0.27 ms    (average over 29,448 calls)
+//       Making one square untraversable:  0.23 ms    (average over 30,123 calls)
+//       Reachability query:               0.00001 ms (average over 4,000,000 calls)
+//
+//   On non-degenerate maps update time is O(N^0.5), but on degenerate maps like
+//   checkerboards or 50% random, update time is O(N^0.75) (~2ms on above machine).
+//
+// CHANGELOG
+//
+//    0.95  (2016-10-16)  Bugfix if multiple clumps in one cluster connect to same clump in another
+//    0.94  (2016-04-17)  Bugfix & optimize worst case (checkerboard & random)
+//    0.93  (2016-04-16)  Reduce memory by 10x for 1Kx1K map; small speedup
+//    0.92  (2016-04-16)  Compute sqrt(N) cluster size by default
+//    0.91  (2016-04-15)  Initial release
+//
+// TODO:
+//    - better API documentation
+//    - more comments
+//    - try re-integrating naive algorithm & compare performance
+//    - more optimized batching (current approach still recomputes local clumps many times)
+//    - function for setting a grid of squares at once (just use batching)
+//
+// LICENSE
+// 
+//   See end of file for license information.
+//
+// ALGORITHM
+//
+//   The NxN grid map is split into sqrt(N) x sqrt(N) blocks called
+//  "clusters". Each cluster independently computes a set of connected
+//   components within that cluster (ignoring all connectivity out of
+//   that cluster) using a union-find disjoint set forest. This produces a bunch
+//   of locally connected components called "clumps". Each clump is (a) connected
+//   within its cluster, (b) does not directly connect to any other clumps in the
+//   cluster (though it may connect to them by paths that lead outside the cluster,
+//   but those are ignored at this step), and (c) maintains an adjacency list of
+//   all clumps in adjacent clusters that it _is_ connected to. Then a second
+//   union-find disjoint set forest is used to compute connected clumps
+//   globally, across the whole map. Reachability is then computed by
+//   finding which clump each input point belongs to, and checking whether
+//   those clumps are in the same "global" connected component.
+//
+//   The above data structure can be updated efficiently; on a change
+//   of a single grid square on the map, only one cluster changes its
+//   purely-local state, so only one cluster needs its clumps fully
+//   recomputed. Clumps in adjacent clusters need their adjacency lists
+//   updated: first to remove all references to the old clumps in the
+//   rebuilt cluster, then to add new references to the new clumps. Both
+//   of these operations can use the existing "find which clump each input
+//   point belongs to" query to compute that adjacency information rapidly.
+
+#ifndef INCLUDE_STB_CONNECTED_COMPONENTS_H
+#define INCLUDE_STB_CONNECTED_COMPONENTS_H
+
+#include <stdlib.h>
+
+typedef struct st_stbcc_grid stbcc_grid;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//
+//  initialization
+//
+
+// you allocate the grid data structure to this size (note that it will be very big!!!)
+extern size_t stbcc_grid_sizeof(void);
+
+// initialize the grid, value of map[] is 0 = traversable, non-0 is solid
+extern void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h);
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//
+//  main functionality
+//
+
+// update a grid square state, 0 = traversable, non-0 is solid
+// i can add a batch-update if it's needed
+extern void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid);
+
+// query if two grid squares are reachable from each other
+extern int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int y2);
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//
+//  bonus functions
+//
+
+// wrap multiple stbcc_update_grid calls in these function to compute
+// multiple updates more efficiently; cannot make queries inside batch
+extern void stbcc_update_batch_begin(stbcc_grid *g);
+extern void stbcc_update_batch_end(stbcc_grid *g);
+
+// query the grid data structure for whether a given square is open or not
+extern int stbcc_query_grid_open(stbcc_grid *g, int x, int y);
+
+// get a unique id for the connected component this is in; it's not necessarily
+// small, you'll need a hash table or something to remap it (or just use
+extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
+#define STBCC_NULL_UNIQUE_ID 0xffffffff // returned for closed map squares
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // INCLUDE_STB_CONNECTED_COMPONENTS_H
+
+#ifdef STB_CONNECTED_COMPONENTS_IMPLEMENTATION
+
+#include <assert.h>
+#include <string.h> // memset
+
+#if !defined(STBCC_GRID_COUNT_X_LOG2) || !defined(STBCC_GRID_COUNT_Y_LOG2)
+   #error "You must define STBCC_GRID_COUNT_X_LOG2 and STBCC_GRID_COUNT_Y_LOG2 to define the max grid supported."
+#endif
+
+#define STBCC__GRID_COUNT_X (1 << STBCC_GRID_COUNT_X_LOG2)
+#define STBCC__GRID_COUNT_Y (1 << STBCC_GRID_COUNT_Y_LOG2)
+
+#define STBCC__MAP_STRIDE   (1 << (STBCC_GRID_COUNT_X_LOG2-3))
+
+#ifndef STBCC_CLUSTER_SIZE_X_LOG2
+   #define STBCC_CLUSTER_SIZE_X_LOG2   (STBCC_GRID_COUNT_X_LOG2/2) // log2(sqrt(2^N)) = 1/2 * log2(2^N)) = 1/2 * N
+   #if STBCC_CLUSTER_SIZE_X_LOG2 > 6
+   #undef STBCC_CLUSTER_SIZE_X_LOG2
+   #define STBCC_CLUSTER_SIZE_X_LOG2 6
+   #endif
+#endif
+
+#ifndef STBCC_CLUSTER_SIZE_Y_LOG2
+   #define STBCC_CLUSTER_SIZE_Y_LOG2   (STBCC_GRID_COUNT_Y_LOG2/2)
+   #if STBCC_CLUSTER_SIZE_Y_LOG2 > 6
+   #undef STBCC_CLUSTER_SIZE_Y_LOG2
+   #define STBCC_CLUSTER_SIZE_Y_LOG2 6
+   #endif
+#endif
+
+#define STBCC__CLUSTER_SIZE_X   (1 << STBCC_CLUSTER_SIZE_X_LOG2)
+#define STBCC__CLUSTER_SIZE_Y   (1 << STBCC_CLUSTER_SIZE_Y_LOG2)
+
+#define STBCC__CLUSTER_COUNT_X_LOG2   (STBCC_GRID_COUNT_X_LOG2 - STBCC_CLUSTER_SIZE_X_LOG2)
+#define STBCC__CLUSTER_COUNT_Y_LOG2   (STBCC_GRID_COUNT_Y_LOG2 - STBCC_CLUSTER_SIZE_Y_LOG2)
+
+#define STBCC__CLUSTER_COUNT_X  (1 << STBCC__CLUSTER_COUNT_X_LOG2)
+#define STBCC__CLUSTER_COUNT_Y  (1 << STBCC__CLUSTER_COUNT_Y_LOG2)
+
+#if STBCC__CLUSTER_SIZE_X >= STBCC__GRID_COUNT_X || STBCC__CLUSTER_SIZE_Y >= STBCC__GRID_COUNT_Y
+   #error "STBCC_CLUSTER_SIZE_X/Y_LOG2 must be smaller than STBCC_GRID_COUNT_X/Y_LOG2"
+#endif
+
+// worst case # of clumps per cluster
+#define STBCC__MAX_CLUMPS_PER_CLUSTER_LOG2   (STBCC_CLUSTER_SIZE_X_LOG2 + STBCC_CLUSTER_SIZE_Y_LOG2-1)
+#define STBCC__MAX_CLUMPS_PER_CLUSTER        (1 << STBCC__MAX_CLUMPS_PER_CLUSTER_LOG2)
+#define STBCC__MAX_CLUMPS                    (STBCC__MAX_CLUMPS_PER_CLUSTER * STBCC__CLUSTER_COUNT_X * STBCC__CLUSTER_COUNT_Y)
+#define STBCC__NULL_CLUMPID                  STBCC__MAX_CLUMPS_PER_CLUSTER
+
+#define STBCC__CLUSTER_X_FOR_COORD_X(x)  ((x) >> STBCC_CLUSTER_SIZE_X_LOG2)
+#define STBCC__CLUSTER_Y_FOR_COORD_Y(y)  ((y) >> STBCC_CLUSTER_SIZE_Y_LOG2)
+
+#define STBCC__MAP_BYTE_MASK(x,y)       (1 << ((x) & 7))
+#define STBCC__MAP_BYTE(g,x,y)          ((g)->map[y][(x) >> 3])
+#define STBCC__MAP_OPEN(g,x,y)          (STBCC__MAP_BYTE(g,x,y) & STBCC__MAP_BYTE_MASK(x,y))
+
+typedef unsigned short stbcc__clumpid;
+typedef unsigned char stbcc__verify_max_clumps[STBCC__MAX_CLUMPS_PER_CLUSTER < (1 << (8*sizeof(stbcc__clumpid))) ? 1 : -1];
+
+#define STBCC__MAX_EXITS_PER_CLUSTER   (STBCC__CLUSTER_SIZE_X + STBCC__CLUSTER_SIZE_Y)   // 64 for 32x32
+#define STBCC__MAX_EXITS_PER_CLUMP     (STBCC__CLUSTER_SIZE_X + STBCC__CLUSTER_SIZE_Y)   // 64 for 32x32
+#define STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER  (STBCC__MAX_EXITS_PER_CLUMP)
+
+// 2^19 * 2^6 => 2^25 exits => 2^26  => 64MB for 1024x1024
+
+// Logic for above on 4x4 grid:
+//
+// Many clumps:      One clump:
+//   + +               +  +
+//  +X.X.             +XX.X+
+//   .X.X+             .XXX
+//  +X.X.              XXX.
+//   .X.X+            +X.XX+
+//    + +              +  +
+//
+// 8 exits either way
+
+typedef unsigned char stbcc__verify_max_exits[STBCC__MAX_EXITS_PER_CLUMP <= 256];
+
+typedef struct
+{
+   unsigned short clump_index:12;
+     signed short cluster_dx:2;
+     signed short cluster_dy:2;
+} stbcc__relative_clumpid;
+
+typedef union
+{
+   struct {
+      unsigned int clump_index:12;
+      unsigned int cluster_x:10;
+      unsigned int cluster_y:10;
+   } f;
+   unsigned int c;
+} stbcc__global_clumpid;
+
+// rebuilt cluster 3,4
+
+// what changes in cluster 2,4
+
+typedef struct
+{
+   stbcc__global_clumpid global_label;        // 4
+   unsigned char num_adjacent;                // 1
+   unsigned char max_adjacent;                // 1
+   unsigned char adjacent_clump_list_index;   // 1
+   unsigned char reserved;
+} stbcc__clump; // 8
+
+#define STBCC__CLUSTER_ADJACENCY_COUNT   (STBCC__MAX_EXITS_PER_CLUSTER*2)
+typedef struct
+{
+   short num_clumps;
+   unsigned char num_edge_clumps;
+   unsigned char rebuild_adjacency;
+   stbcc__clump clump[STBCC__MAX_CLUMPS_PER_CLUSTER];       // 8 * 2^9 = 4KB
+   stbcc__relative_clumpid adjacency_storage[STBCC__CLUSTER_ADJACENCY_COUNT]; // 256 bytes
+} stbcc__cluster;
+
+struct st_stbcc_grid
+{
+   int w,h,cw,ch;
+   int in_batched_update;
+   //unsigned char cluster_dirty[STBCC__CLUSTER_COUNT_Y][STBCC__CLUSTER_COUNT_X]; // could bitpack, but: 1K x 1K => 1KB
+   unsigned char map[STBCC__GRID_COUNT_Y][STBCC__MAP_STRIDE]; // 1K x 1K => 1K x 128 => 128KB
+   stbcc__clumpid clump_for_node[STBCC__GRID_COUNT_Y][STBCC__GRID_COUNT_X];  // 1K x 1K x 2 = 2MB
+   stbcc__cluster cluster[STBCC__CLUSTER_COUNT_Y][STBCC__CLUSTER_COUNT_X]; //  1K x 4.5KB = 4.5MB
+};
+
+int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
+{
+   stbcc__global_clumpid label1, label2;
+   stbcc__clumpid c1 = g->clump_for_node[y1][x1];
+   stbcc__clumpid c2 = g->clump_for_node[y2][x2];
+   int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
+   int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1);
+   int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2);
+   int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
+   assert(!g->in_batched_update);
+   if (c1 == STBCC__NULL_CLUMPID || c2 == STBCC__NULL_CLUMPID)
+      return 0;
+   label1 = g->cluster[cy1][cx1].clump[c1].global_label;
+   label2 = g->cluster[cy2][cx2].clump[c2].global_label;
+   if (label1.c == label2.c)
+      return 1;
+   return 0;
+}
+
+int stbcc_query_grid_open(stbcc_grid *g, int x, int y)
+{
+   return STBCC__MAP_OPEN(g, x, y) != 0;
+}
+
+unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y)
+{
+   stbcc__clumpid c = g->clump_for_node[y][x];
+   int cx = STBCC__CLUSTER_X_FOR_COORD_X(x);
+   int cy = STBCC__CLUSTER_Y_FOR_COORD_Y(y);
+   assert(!g->in_batched_update);
+   if (c == STBCC__NULL_CLUMPID) return STBCC_NULL_UNIQUE_ID;
+   return g->cluster[cy][cx].clump[c].global_label.c;
+}
+
+typedef struct
+{
+   unsigned char x,y;
+} stbcc__tinypoint;
+
+typedef struct
+{
+   stbcc__tinypoint parent[STBCC__CLUSTER_SIZE_Y][STBCC__CLUSTER_SIZE_X]; // 32x32 => 2KB
+   stbcc__clumpid   label[STBCC__CLUSTER_SIZE_Y][STBCC__CLUSTER_SIZE_X];
+} stbcc__cluster_build_info;
+
+static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy);
+static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy);
+static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy);
+
+static stbcc__global_clumpid stbcc__clump_find(stbcc_grid *g, stbcc__global_clumpid n)
+{
+   stbcc__global_clumpid q;
+   stbcc__clump *c = &g->cluster[n.f.cluster_y][n.f.cluster_x].clump[n.f.clump_index];
+
+   if (c->global_label.c == n.c)
+      return n;
+
+   q = stbcc__clump_find(g, c->global_label);
+   c->global_label = q;
+   return q;
+}
+
+typedef struct
+{
+   unsigned int cluster_x;
+   unsigned int cluster_y;
+   unsigned int clump_index;
+} stbcc__unpacked_clumpid;
+
+static void stbcc__clump_union(stbcc_grid *g, stbcc__unpacked_clumpid m, int x, int y, int idx)
+{
+   stbcc__clump *mc = &g->cluster[m.cluster_y][m.cluster_x].clump[m.clump_index];
+   stbcc__clump *nc = &g->cluster[y][x].clump[idx];
+   stbcc__global_clumpid mp = stbcc__clump_find(g, mc->global_label);
+   stbcc__global_clumpid np = stbcc__clump_find(g, nc->global_label);
+
+   if (mp.c == np.c)
+      return;
+
+   g->cluster[mp.f.cluster_y][mp.f.cluster_x].clump[mp.f.clump_index].global_label = np;
+}
+
+static void stbcc__build_connected_components_for_clumps(stbcc_grid *g)
+{
+   int i,j,k,h;
+
+   for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
+      for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
+         stbcc__cluster *cluster = &g->cluster[j][i];
+         for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
+            stbcc__global_clumpid m;
+            m.f.clump_index = k;
+            m.f.cluster_x = i;
+            m.f.cluster_y = j;
+            assert((int) m.f.clump_index == k && (int) m.f.cluster_x == i && (int) m.f.cluster_y == j);
+            cluster->clump[k].global_label = m;
+         }
+      }
+   }
+
+   for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
+      for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
+         stbcc__cluster *cluster = &g->cluster[j][i];
+         for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
+            stbcc__clump *clump = &cluster->clump[k];
+            stbcc__unpacked_clumpid m;
+            stbcc__relative_clumpid *adj;
+            m.clump_index = k;
+            m.cluster_x = i;
+            m.cluster_y = j;
+            adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
+            for (h=0; h < clump->num_adjacent; ++h) {
+               unsigned int clump_index = adj[h].clump_index;
+               unsigned int x = adj[h].cluster_dx + i;
+               unsigned int y = adj[h].cluster_dy + j;
+               stbcc__clump_union(g, m, x, y, clump_index);
+            }
+         }
+      }
+   }
+
+   for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
+      for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
+         stbcc__cluster *cluster = &g->cluster[j][i];
+         for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
+            stbcc__global_clumpid m;
+            m.f.clump_index = k;
+            m.f.cluster_x = i;
+            m.f.cluster_y = j;
+            stbcc__clump_find(g, m);
+         }
+      }
+   }
+}
+
+static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int cy)
+{
+   // in this particular case, we are fully non-incremental. that means we
+   // can discover the correct sizes for the arrays, but requires we build
+   // the data into temporary data structures, or just count the sizes, so
+   // for simplicity we do the latter
+   stbcc__cluster *cluster = &g->cluster[cy][cx];
+   unsigned char connected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8]; // 64 x 8 => 1KB
+   unsigned char num_adj[STBCC__MAX_CLUMPS_PER_CLUSTER] = { 0 };
+   int x = cx * STBCC__CLUSTER_SIZE_X;
+   int y = cy * STBCC__CLUSTER_SIZE_Y;
+   int step_x, step_y=0, i, j, k, n, m, dx, dy, total;
+   int extra;
+
+   g->cluster[cy][cx].rebuild_adjacency = 0;
+
+   total = 0;
+   for (m=0; m < 4; ++m) {
+      switch (m) {
+         case 0:
+            dx = 1, dy = 0;
+            step_x = 0, step_y = 1;
+            i = STBCC__CLUSTER_SIZE_X-1;
+            j = 0;
+            n = STBCC__CLUSTER_SIZE_Y;
+            break;
+         case 1:
+            dx = -1, dy = 0;
+            i = 0;
+            j = 0;
+            step_x = 0;
+            step_y = 1;  
+            n = STBCC__CLUSTER_SIZE_Y;
+            break;
+         case 2:
+            dy = -1, dx = 0;
+            i = 0;
+            j = 0;
+            step_x = 1;
+            step_y = 0;
+            n = STBCC__CLUSTER_SIZE_X;
+            break;
+         case 3:
+            dy = 1, dx = 0;
+            i = 0;
+            j = STBCC__CLUSTER_SIZE_Y-1;
+            step_x = 1;
+            step_y = 0;
+            n = STBCC__CLUSTER_SIZE_X;
+            break;
+      }
+
+      if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
+         continue;
+
+      memset(connected, 0, sizeof(connected));
+      for (k=0; k < n; ++k) {
+         if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
+            stbcc__clumpid src = g->clump_for_node[y+j][x+i];
+            stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
+            if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
+               connected[src][dest>>3] |= 1 << (dest & 7);
+               ++num_adj[src];
+               ++total;
+            }
+         }
+         i += step_x;
+         j += step_y;
+      }
+   }
+
+   assert(total <= STBCC__CLUSTER_ADJACENCY_COUNT);
+
+   // decide how to apportion unused adjacency slots; only clumps that lie
+   // on the edges of the cluster need adjacency slots, so divide them up
+   // evenly between those clumps
+
+   // we want:
+   //    extra = (STBCC__CLUSTER_ADJACENCY_COUNT - total) / cluster->num_edge_clumps;
+   // but we efficiently approximate this without a divide, because
+   // ignoring edge-vs-non-edge with 'num_adj[i]*2' was faster than
+   // 'num_adj[i]+extra' with the divide
+   if      (total + (cluster->num_edge_clumps<<2) <= STBCC__CLUSTER_ADJACENCY_COUNT)
+      extra = 4;
+   else if (total + (cluster->num_edge_clumps<<1) <= STBCC__CLUSTER_ADJACENCY_COUNT)
+      extra = 2;
+   else if (total + (cluster->num_edge_clumps<<0) <= STBCC__CLUSTER_ADJACENCY_COUNT)
+      extra = 1;
+   else
+      extra = 0;
+
+   total = 0;
+   for (i=0; i < (int) cluster->num_edge_clumps; ++i) {
+      int alloc = num_adj[i]+extra;
+      if (alloc > STBCC__MAX_EXITS_PER_CLUSTER)
+         alloc = STBCC__MAX_EXITS_PER_CLUSTER;
+      assert(total < 256); // must fit in byte
+      cluster->clump[i].adjacent_clump_list_index = (unsigned char) total;
+      cluster->clump[i].max_adjacent = alloc;
+      cluster->clump[i].num_adjacent = 0;
+      total += alloc;
+   }
+   assert(total <= STBCC__CLUSTER_ADJACENCY_COUNT);
+
+   stbcc__add_connections_to_adjacent_cluster(g, cx, cy, -1, 0);
+   stbcc__add_connections_to_adjacent_cluster(g, cx, cy,  1, 0);
+   stbcc__add_connections_to_adjacent_cluster(g, cx, cy,  0,-1);
+   stbcc__add_connections_to_adjacent_cluster(g, cx, cy,  0, 1);
+   // make sure all of the above succeeded.
+   assert(g->cluster[cy][cx].rebuild_adjacency == 0);
+}
+
+static void stbcc__add_connections_to_adjacent_cluster_with_rebuild(stbcc_grid *g, int cx, int cy, int dx, int dy)
+{
+   if (cx >= 0 && cx < g->cw && cy >= 0 && cy < g->ch) {
+      stbcc__add_connections_to_adjacent_cluster(g, cx, cy, dx, dy);
+      if (g->cluster[cy][cx].rebuild_adjacency)
+         stbcc__build_all_connections_for_cluster(g, cx, cy);
+   }
+}
+
+void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
+{
+   int cx,cy;
+
+   if (!solid) {
+      if (STBCC__MAP_OPEN(g,x,y))
+         return;
+   } else {
+      if (!STBCC__MAP_OPEN(g,x,y))
+         return;
+   }
+
+   cx = STBCC__CLUSTER_X_FOR_COORD_X(x);
+   cy = STBCC__CLUSTER_Y_FOR_COORD_Y(y);
+
+   stbcc__remove_connections_to_adjacent_cluster(g, cx-1, cy,  1, 0);
+   stbcc__remove_connections_to_adjacent_cluster(g, cx+1, cy, -1, 0);
+   stbcc__remove_connections_to_adjacent_cluster(g, cx, cy-1,  0, 1);
+   stbcc__remove_connections_to_adjacent_cluster(g, cx, cy+1,  0,-1);
+
+   if (!solid)
+      STBCC__MAP_BYTE(g,x,y) |= STBCC__MAP_BYTE_MASK(x,y);
+   else
+      STBCC__MAP_BYTE(g,x,y) &= ~STBCC__MAP_BYTE_MASK(x,y);
+
+   stbcc__build_clumps_for_cluster(g, cx, cy);
+   stbcc__build_all_connections_for_cluster(g, cx, cy);
+
+   stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx-1, cy,  1, 0);
+   stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx+1, cy, -1, 0);
+   stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx, cy-1,  0, 1);
+   stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx, cy+1,  0,-1);
+
+   if (!g->in_batched_update)
+      stbcc__build_connected_components_for_clumps(g);
+   #if 0
+   else
+      g->cluster_dirty[cy][cx] = 1;
+   #endif
+}
+
+void stbcc_update_batch_begin(stbcc_grid *g)
+{
+   assert(!g->in_batched_update);
+   g->in_batched_update = 1;
+}
+
+void stbcc_update_batch_end(stbcc_grid *g)
+{
+   assert(g->in_batched_update);
+   g->in_batched_update =  0;
+   stbcc__build_connected_components_for_clumps(g); // @OPTIMIZE: only do this if update was non-empty
+}
+
+size_t stbcc_grid_sizeof(void)
+{
+   return sizeof(stbcc_grid);
+}
+
+void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h)
+{
+   int i,j,k;
+   assert(w % STBCC__CLUSTER_SIZE_X == 0);
+   assert(h % STBCC__CLUSTER_SIZE_Y == 0);
+   assert(w % 8 == 0);
+
+   g->w = w;
+   g->h = h;
+   g->cw = w >> STBCC_CLUSTER_SIZE_X_LOG2;
+   g->ch = h >> STBCC_CLUSTER_SIZE_Y_LOG2;
+   g->in_batched_update = 0;
+
+   #if 0
+   for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j)
+      for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) 
+         g->cluster_dirty[j][i] = 0;
+   #endif
+
+   for (j=0; j < h; ++j) {
+      for (i=0; i < w; i += 8) {
+         unsigned char c = 0;
+         for (k=0; k < 8; ++k)
+            if (map[j*w + (i+k)] == 0)
+               c |= (1 << k);
+         g->map[j][i>>3] = c;
+      }
+   }
+
+   for (j=0; j < g->ch; ++j)
+      for (i=0; i < g->cw; ++i)
+         stbcc__build_clumps_for_cluster(g, i, j);
+
+   for (j=0; j < g->ch; ++j)
+      for (i=0; i < g->cw; ++i)
+         stbcc__build_all_connections_for_cluster(g, i, j);
+
+   stbcc__build_connected_components_for_clumps(g);
+
+   for (j=0; j < g->h; ++j)
+      for (i=0; i < g->w; ++i)
+         assert(g->clump_for_node[j][i] <= STBCC__NULL_CLUMPID);
+}
+
+
+static void stbcc__add_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
+{
+   stbcc__cluster *cluster;
+   stbcc__clump *clump;
+
+   int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
+   int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1);
+   int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2);
+   int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
+
+   stbcc__clumpid c1 = g->clump_for_node[y1][x1];
+   stbcc__clumpid c2 = g->clump_for_node[y2][x2];
+
+   stbcc__relative_clumpid rc;
+
+   assert(cx1 != cx2 || cy1 != cy2);
+   assert(abs(cx1-cx2) + abs(cy1-cy2) == 1);
+
+   // add connection to c2 in c1
+
+   rc.clump_index = c2;
+   rc.cluster_dx = x2-x1;
+   rc.cluster_dy = y2-y1;
+
+   cluster = &g->cluster[cy1][cx1];
+   clump = &cluster->clump[c1];
+   assert(clump->num_adjacent <= clump->max_adjacent);
+   if (clump->num_adjacent == clump->max_adjacent)
+      g->cluster[cy1][cx1].rebuild_adjacency = 1;
+   else {
+      stbcc__relative_clumpid *adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
+      assert(clump->num_adjacent < STBCC__MAX_EXITS_PER_CLUMP);
+      assert(clump->adjacent_clump_list_index + clump->num_adjacent <= STBCC__CLUSTER_ADJACENCY_COUNT);
+      adj[clump->num_adjacent++] = rc;
+   }
+}
+
+static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2, int y2)
+{
+   stbcc__cluster *cluster;
+   stbcc__clump *clump;
+   stbcc__relative_clumpid *adj;
+   int i;
+
+   int cx1 = STBCC__CLUSTER_X_FOR_COORD_X(x1);
+   int cy1 = STBCC__CLUSTER_Y_FOR_COORD_Y(y1);
+   int cx2 = STBCC__CLUSTER_X_FOR_COORD_X(x2);
+   int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
+
+   stbcc__clumpid c1 = g->clump_for_node[y1][x1];
+   stbcc__clumpid c2 = g->clump_for_node[y2][x2];
+
+   stbcc__relative_clumpid rc;
+
+   assert(cx1 != cx2 || cy1 != cy2);
+   assert(abs(cx1-cx2) + abs(cy1-cy2) == 1);
+
+   // add connection to c2 in c1
+
+   rc.clump_index = c2;
+   rc.cluster_dx = x2-x1;
+   rc.cluster_dy = y2-y1;
+
+   cluster = &g->cluster[cy1][cx1];
+   clump = &cluster->clump[c1];
+   adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
+
+   for (i=0; i < clump->num_adjacent; ++i)
+      if (rc.clump_index == adj[i].clump_index &&
+          rc.cluster_dx  == adj[i].cluster_dx  &&
+          rc.cluster_dy  == adj[i].cluster_dy) 
+         break;
+
+   if (i < clump->num_adjacent)
+      adj[i] = adj[--clump->num_adjacent];
+   else
+      assert(0);
+}
+
+static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
+{
+   unsigned char connected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { 0 };
+   int x = cx * STBCC__CLUSTER_SIZE_X;
+   int y = cy * STBCC__CLUSTER_SIZE_Y;
+   int step_x, step_y=0, i, j, k, n;
+
+   if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
+      return;
+
+   if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
+      return;
+
+   if (g->cluster[cy][cx].rebuild_adjacency)
+      return;
+
+   assert(abs(dx) + abs(dy) == 1);
+
+   if (dx == 1) {
+      i = STBCC__CLUSTER_SIZE_X-1;
+      j = 0;
+      step_x = 0;
+      step_y = 1;
+      n = STBCC__CLUSTER_SIZE_Y;
+   } else if (dx == -1) {
+      i = 0;
+      j = 0;
+      step_x = 0;
+      step_y = 1;  
+      n = STBCC__CLUSTER_SIZE_Y;
+   } else if (dy == -1) {
+      i = 0;
+      j = 0;
+      step_x = 1;
+      step_y = 0;
+      n = STBCC__CLUSTER_SIZE_X;
+   } else if (dy == 1) {
+      i = 0;
+      j = STBCC__CLUSTER_SIZE_Y-1;
+      step_x = 1;
+      step_y = 0;
+      n = STBCC__CLUSTER_SIZE_X;
+   } else {
+      assert(0);
+   }
+
+   for (k=0; k < n; ++k) {
+      if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
+         stbcc__clumpid src = g->clump_for_node[y+j][x+i];
+         stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
+         if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
+            assert((dest>>3) < sizeof(connected));
+            connected[src][dest>>3] |= 1 << (dest & 7);
+            stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
+            if (g->cluster[cy][cx].rebuild_adjacency)
+               break;
+         }
+      }
+      i += step_x;
+      j += step_y;
+   }
+}
+
+static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
+{
+   unsigned char disconnected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { 0 };
+   int x = cx * STBCC__CLUSTER_SIZE_X;
+   int y = cy * STBCC__CLUSTER_SIZE_Y;
+   int step_x, step_y=0, i, j, k, n;
+
+   if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
+      return;
+
+   if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
+      return;
+
+   assert(abs(dx) + abs(dy) == 1);
+
+   if (dx == 1) {
+      i = STBCC__CLUSTER_SIZE_X-1;
+      j = 0;
+      step_x = 0;
+      step_y = 1;
+      n = STBCC__CLUSTER_SIZE_Y;
+   } else if (dx == -1) {
+      i = 0;
+      j = 0;
+      step_x = 0;
+      step_y = 1;  
+      n = STBCC__CLUSTER_SIZE_Y;
+   } else if (dy == -1) {
+      i = 0;
+      j = 0;
+      step_x = 1;
+      step_y = 0;
+      n = STBCC__CLUSTER_SIZE_X;
+   } else if (dy == 1) {
+      i = 0;
+      j = STBCC__CLUSTER_SIZE_Y-1;
+      step_x = 1;
+      step_y = 0;
+      n = STBCC__CLUSTER_SIZE_X;
+   } else {
+      assert(0);
+   }
+
+   for (k=0; k < n; ++k) {
+      if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
+         stbcc__clumpid src = g->clump_for_node[y+j][x+i];
+         stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
+         if (0 == (disconnected[src][dest>>3] & (1 << (dest & 7)))) {
+            disconnected[src][dest>>3] |= 1 << (dest & 7);
+            stbcc__remove_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
+         }
+      }
+      i += step_x;
+      j += step_y;
+   }
+}
+
+static stbcc__tinypoint stbcc__incluster_find(stbcc__cluster_build_info *cbi, int x, int y)
+{
+   stbcc__tinypoint p,q;
+   p = cbi->parent[y][x];
+   if (p.x == x && p.y == y)
+      return p;
+   q = stbcc__incluster_find(cbi, p.x, p.y);
+   cbi->parent[y][x] = q;
+   return q;
+}
+
+static void stbcc__incluster_union(stbcc__cluster_build_info *cbi, int x1, int y1, int x2, int y2)
+{
+   stbcc__tinypoint p = stbcc__incluster_find(cbi, x1,y1);
+   stbcc__tinypoint q = stbcc__incluster_find(cbi, x2,y2);
+
+   if (p.x == q.x && p.y == q.y)
+      return;
+
+   cbi->parent[p.y][p.x] = q;
+}
+
+static void stbcc__switch_root(stbcc__cluster_build_info *cbi, int x, int y, stbcc__tinypoint p)
+{
+   cbi->parent[p.y][p.x].x = x;
+   cbi->parent[p.y][p.x].y = y;
+   cbi->parent[y][x].x = x;
+   cbi->parent[y][x].y = y;
+}
+
+static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
+{
+   stbcc__cluster *c;
+   stbcc__cluster_build_info cbi;
+   int label=0;
+   int i,j;
+   int x = cx * STBCC__CLUSTER_SIZE_X;
+   int y = cy * STBCC__CLUSTER_SIZE_Y;
+
+   // set initial disjoint set forest state
+   for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
+      for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
+         cbi.parent[j][i].x = i;
+         cbi.parent[j][i].y = j;
+      }
+   }
+
+   // join all sets that are connected
+   for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
+      // check down only if not on bottom row
+      if (j < STBCC__CLUSTER_SIZE_Y-1)
+         for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
+            if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i  ,y+j+1))
+               stbcc__incluster_union(&cbi, i,j, i,j+1);
+      // check right for everything but rightmost column
+      for (i=0; i < STBCC__CLUSTER_SIZE_X-1; ++i)
+         if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i+1,y+j  ))
+            stbcc__incluster_union(&cbi, i,j, i+1,j);
+   }
+
+   // label all non-empty clumps along edges so that all edge clumps are first
+   // in list; this means in degenerate case we can skip traversing non-edge clumps.
+   // because in the first pass we only label leaders, we swap the leader to the
+   // edge first
+
+   // first put solid labels on all the edges; these will get overwritten if they're open
+   for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j)
+      cbi.label[j][0] = cbi.label[j][STBCC__CLUSTER_SIZE_X-1] = STBCC__NULL_CLUMPID;
+   for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
+      cbi.label[0][i] = cbi.label[STBCC__CLUSTER_SIZE_Y-1][i] = STBCC__NULL_CLUMPID;
+
+   for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
+      i = 0;
+      if (STBCC__MAP_OPEN(g, x+i, y+j)) {
+         stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
+         if (p.x == i && p.y == j)
+            // if this is the leader, give it a label
+            cbi.label[j][i] = label++;
+         else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
+            // if leader is in interior, promote this edge node to leader and label
+            stbcc__switch_root(&cbi, i, j, p);
+            cbi.label[j][i] = label++;
+         }
+         // else if leader is on edge, do nothing (it'll get labelled when we reach it)
+      }
+      i = STBCC__CLUSTER_SIZE_X-1;
+      if (STBCC__MAP_OPEN(g, x+i, y+j)) {
+         stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
+         if (p.x == i && p.y == j)
+            cbi.label[j][i] = label++;
+         else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
+            stbcc__switch_root(&cbi, i, j, p);
+            cbi.label[j][i] = label++;
+         }
+      }
+   }
+
+   for (i=1; i < STBCC__CLUSTER_SIZE_Y-1; ++i) {
+      j = 0;
+      if (STBCC__MAP_OPEN(g, x+i, y+j)) {
+         stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
+         if (p.x == i && p.y == j)
+            cbi.label[j][i] = label++;
+         else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
+            stbcc__switch_root(&cbi, i, j, p);
+            cbi.label[j][i] = label++;
+         }
+      }
+      j = STBCC__CLUSTER_SIZE_Y-1;
+      if (STBCC__MAP_OPEN(g, x+i, y+j)) {
+         stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
+         if (p.x == i && p.y == j)
+            cbi.label[j][i] = label++;
+         else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
+            stbcc__switch_root(&cbi, i, j, p);
+            cbi.label[j][i] = label++;
+         }
+      }
+   }
+
+   c = &g->cluster[cy][cx];
+   c->num_edge_clumps = label;
+
+   // label any internal clusters
+   for (j=1; j < STBCC__CLUSTER_SIZE_Y-1; ++j) {
+      for (i=1; i < STBCC__CLUSTER_SIZE_X-1; ++i) {
+         stbcc__tinypoint p = cbi.parent[j][i];
+         if (p.x == i && p.y == j)
+            if (STBCC__MAP_OPEN(g,x+i,y+j))
+               cbi.label[j][i] = label++;
+            else
+               cbi.label[j][i] = STBCC__NULL_CLUMPID;
+      }
+   }
+
+   // label all other nodes
+   for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
+      for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
+         stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
+         if (p.x != i || p.y != j) {
+            if (STBCC__MAP_OPEN(g,x+i,y+j))
+               cbi.label[j][i] = cbi.label[p.y][p.x];
+         }
+         if (STBCC__MAP_OPEN(g,x+i,y+j))
+            assert(cbi.label[j][i] != STBCC__NULL_CLUMPID);
+      }
+   }
+
+   c->num_clumps = label;
+
+   for (i=0; i < label; ++i) {
+      c->clump[i].num_adjacent = 0;
+      c->clump[i].max_adjacent = 0;
+   }
+
+   for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j)
+      for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
+         g->clump_for_node[y+j][x+i] = cbi.label[j][i]; // @OPTIMIZE: remove cbi.label entirely
+         assert(g->clump_for_node[y+j][x+i] <= STBCC__NULL_CLUMPID);
+      }
+
+   // set the global label for all interior clumps since they can't have connections,
+   // so we don't have to do this on the global pass (brings from O(N) to O(N^0.75))
+   for (i=(int) c->num_edge_clumps; i < (int) c->num_clumps; ++i) {
+      stbcc__global_clumpid gc;
+      gc.f.cluster_x = cx;
+      gc.f.cluster_y = cy;
+      gc.f.clump_index = i;
+      c->clump[i].global_label = gc;
+   }
+
+   c->rebuild_adjacency = 1; // flag that it has no valid adjacency data
+}
+
+#endif // STB_CONNECTED_COMPONENTS_IMPLEMENTATION
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
+software, either in source code form or as a compiled binary, for any purpose, 
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this 
+software dedicate any and all copyright interest in the software to the public 
+domain. We make this dedication for the benefit of the public at large and to 
+the detriment of our heirs and successors. We intend this dedication to be an 
+overt act of relinquishment in perpetuity of all present and future rights to 
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/

+ 46 - 0
stb_divide.h

@@ -75,6 +75,10 @@
 // by the euclidean division operator we define, so it's possibly not
 // always true. If any such platform turns up, we can add more cases.
 // (Possibly only stb_div_trunc currently relies on property (b).)
+//
+// LICENSE
+//
+//   See end of file for license information.
 
 
 #ifndef INCLUDE_STB_DIVIDE_H
@@ -371,3 +375,45 @@ int main(int argc, char **argv)
 #endif // STB_DIVIDE_TEST
 #endif // STB_DIVIDE_IMPLEMENTATION
 #endif // INCLUDE_STB_DIVIDE_H
+
+/*
+------------------------------------------------------------------------------
+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.
+------------------------------------------------------------------------------
+*/

+ 72 - 9
stb_dxt.h

@@ -1,4 +1,4 @@
-// stb_dxt.h - v1.04 - DXT1/DXT5 compressor - public domain
+// stb_dxt.h - v1.06 - DXT1/DXT5 compressor - public domain
 // original by fabian "ryg" giesen - ported to C by stb
 // use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation
 //
@@ -9,6 +9,8 @@
 //     and "high quality" using mode.
 //
 // version history:
+//   v1.06  - (stb) fix to known-broken 1.05
+//   v1.05  - (stb) support bc5/3dc (Arvids Kokins), use extern "C" in C++ (Pavel Krajcevski)
 //   v1.04  - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec);
 //            single color match fix (allow for inexact color interpolation);
 //            optimal DXT5 index finder; "high quality" mode that runs multiple refinement steps.
@@ -16,6 +18,10 @@
 //   v1.02  - (stb) fix alpha encoding bug
 //   v1.01  - (stb) fix bug converting to RGB that messed up quality, thanks ryg & cbloom
 //   v1.00  - (stb) first release
+//
+// LICENSE
+//
+//   See end of file for license information.
 
 #ifndef STB_INCLUDE_STB_DXT_H
 #define STB_INCLUDE_STB_DXT_H
@@ -25,7 +31,17 @@
 #define STB_DXT_DITHER    1   // use dithering. dubious win. never use for normal maps and the like!
 #define STB_DXT_HIGHQUAL  2   // high quality mode, does two refinement steps instead of 1. ~30-40% slower.
 
-void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode);
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src_rgba_four_bytes_per_pixel, int alpha, int mode);
+void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src_rg_two_byte_per_pixel);
+
+#ifdef __cplusplus
+}
+#endif
+
 #define STB_COMPRESS_DXT_BLOCK
 
 #ifdef STB_DXT_IMPLEMENTATION
@@ -532,18 +548,18 @@ static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block, i
 }
 
 // Alpha block compression (this is easy for a change)
-static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src,int mode)
+static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src, int stride)
 {
    int i,dist,bias,dist4,dist2,bits,mask;
 
    // find min/max color
    int mn,mx;
-   mn = mx = src[3];
+   mn = mx = src[0];
 
    for (i=1;i<16;i++)
    {
-      if (src[i*4+3] < mn) mn = src[i*4+3];
-      else if (src[i*4+3] > mx) mx = src[i*4+3];
+      if (src[i*stride] < mn) mn = src[i*stride];
+      else if (src[i*stride] > mx) mx = src[i*stride];
    }
 
    // encode them
@@ -562,7 +578,7 @@ static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src,int m
    bits = 0,mask=0;
    
    for (i=0;i<16;i++) {
-      int a = src[i*4+3]*7 + bias;
+      int a = src[i*stride]*7 + bias;
       int ind,t;
 
       // select index. this is a "linear scale" lerp factor between 0 (val=min) and 7 (val=max).
@@ -613,12 +629,59 @@ void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int a
    }
 
    if (alpha) {
-      stb__CompressAlphaBlock(dest,(unsigned char*) src,mode);
+      stb__CompressAlphaBlock(dest,(unsigned char*) src+3, 4);
       dest += 8;
    }
 
    stb__CompressColorBlock(dest,(unsigned char*) src,mode);
 }
-#endif // STB_DXT_IMPLEMENTATION
 
+void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src)
+{
+   stb__CompressAlphaBlock(dest,(unsigned char*) src,2);
+   stb__CompressAlphaBlock(dest + 8,(unsigned char*) src+1,2);
+}
+#endif // STB_DXT_IMPLEMENTATION
 #endif // STB_INCLUDE_STB_DXT_H
+
+/*
+------------------------------------------------------------------------------
+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.
+------------------------------------------------------------------------------
+*/

+ 93 - 10
stb_easy_font.h

@@ -1,4 +1,4 @@
-// stb_easy_font.h - v0.5 - bitmap font for 3D rendering - public domain
+// stb_easy_font.h - v1.0 - bitmap font for 3D rendering - public domain
 // Sean Barrett, Feb 2015
 //
 //    Easy-to-deploy,
@@ -16,8 +16,10 @@
 // DOCUMENTATION:
 //
 //   int stb_easy_font_width(char *text)
+//   int stb_easy_font_height(char *text)
 //
-//      Takes a string without newlines and returns the horizontal size.
+//      Takes a string and returns the horizontal size and the
+//      vertical size (which can vary if 'text' has newlines).
 //
 //   int stb_easy_font_print(float x, float y,
 //                           char *text, unsigned char color[4],
@@ -40,7 +42,7 @@
 //
 //      You can ignore z and color if you get them from elsewhere
 //      This format was chosen in the hopes it would make it
-//      easier for you to reuse existing buffer-drawing code.
+//      easier for you to reuse existing vertex-buffer-drawing code.
 //
 //      If you pass in NULL for color, it becomes 255,255,255,255.
 //
@@ -63,12 +65,27 @@
 //      compact to me; -0.5 is a reasonable compromise as long as
 //      you're scaling the font up.
 //
+// LICENSE
+//
+//   See end of file for license information.
+//
+// VERSION HISTORY
+//
+//   (2017-01-15)  1.0   space character takes same space as numbers; fix bad spacing of 'f'
+//   (2016-01-22)  0.7   width() supports multiline text; add height()
+//   (2015-09-13)  0.6   #include <math.h>; updated license
+//   (2015-02-01)  0.5   First release
+//
+// CONTRIBUTORS
+//
+//   github:vassvik  --  bug report
+
+#if 0
 // SAMPLE CODE:
 //
 //    Here's sample code for old OpenGL; it's a lot more complicated
 //    to make work on modern APIs, and that's your problem.
 //
-#if 0
 void print_string(float x, float y, char *text, float r, float g, float b)
 {
   static char buffer[99999]; // ~500 chars
@@ -88,13 +105,14 @@ void print_string(float x, float y, char *text, float r, float g, float b)
 #define INCLUDE_STB_EASY_FONT_H
 
 #include <stdlib.h>
+#include <math.h>
 
-struct {
+struct stb_easy_font_info_struct {
     unsigned char advance;
     unsigned char h_seg;
     unsigned char v_seg;
 } stb_easy_font_charinfo[96] = {
-    {  5,  0,  0 },  {  3,  0,  0 },  {  5,  1,  1 },  {  7,  1,  4 },
+    {  6,  0,  0 },  {  3,  0,  0 },  {  5,  1,  1 },  {  7,  1,  4 },
     {  7,  3,  7 },  {  7,  6, 12 },  {  7,  8, 19 },  {  4, 16, 21 },
     {  4, 17, 22 },  {  4, 19, 23 },  { 23, 21, 24 },  { 23, 22, 31 },
     { 20, 23, 34 },  { 22, 23, 36 },  { 19, 24, 36 },  { 21, 25, 36 },
@@ -111,7 +129,7 @@ struct {
     {  7,109,165 },  {  7,118,167 },  {  6,118,172 },  {  4,120,176 },
     {  6,122,177 },  {  4,122,181 },  { 23,124,182 },  { 22,129,182 },
     {  4,130,182 },  { 22,131,183 },  {  6,133,187 },  { 22,135,191 },
-    {  6,137,192 },  { 22,139,196 },  {  5,144,197 },  { 22,147,198 },
+    {  6,137,192 },  { 22,139,196 },  {  6,144,197 },  { 22,147,198 },
     {  6,150,202 },  { 19,151,206 },  { 21,152,207 },  {  6,155,209 },
     {  3,160,210 },  { 23,160,211 },  { 22,164,216 },  { 22,165,220 },
     { 22,167,224 },  { 22,169,228 },  { 21,171,232 },  { 21,173,233 },
@@ -210,11 +228,76 @@ static int stb_easy_font_print(float x, float y, char *text, unsigned char color
 static int stb_easy_font_width(char *text)
 {
     float len = 0;
+    float max_len = 0;
+    while (*text) {
+        if (*text == '\n') {
+            if (len > max_len) max_len = len;
+            len = 0;
+        } else {
+            len += stb_easy_font_charinfo[*text-32].advance & 15;
+            len += stb_easy_font_spacing_val;
+        }
+        ++text;
+    }
+    if (len > max_len) max_len = len;
+    return (int) ceil(max_len);
+}
+
+static int stb_easy_font_height(char *text)
+{
+    float y = 0;
+    int nonempty_line=0;
     while (*text) {
-        len += stb_easy_font_charinfo[*text-32].advance & 15;
-        len += stb_easy_font_spacing_val;
+        if (*text == '\n') {
+            y += 12;
+            nonempty_line = 0;
+        } else {
+            nonempty_line = 1;
+        }
         ++text;
     }
-    return (int) ceil(len);
+    return (int) ceil(y + (nonempty_line ? 12 : 0));
 }
 #endif
+
+/*
+------------------------------------------------------------------------------
+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.
+------------------------------------------------------------------------------
+*/

+ 5 - 2
stb_herringbone_wang_tile.h

@@ -1,8 +1,11 @@
 /* stbhw - v0.6 -  http://nothings.org/gamedev/herringbone
    Herringbone Wang Tile Generator - Sean Barrett 2014 - public domain
 
- This file is in the public domain. In case that declaration is ineffective,
- you are also granted a license to use and modify it without restriction.
+== LICENSE ==============================
+
+This software is dual-licensed to the public domain and under the following
+license: you are granted a perpetual, irrevocable license to copy, modify,
+publish, and distribute this file as you see fit.
 
 == WHAT IT IS ===========================
 

文件差異過大導致無法顯示
+ 402 - 250
stb_image.h


+ 170 - 131
stb_image_resize.h

@@ -1,4 +1,4 @@
-/* stb_image_resize - v0.90 - public domain image resizing
+/* stb_image_resize - v0.94 - public domain image resizing
    by Jorge L Rodriguez (@VinoBS) - 2014
    http://github.com/nothings/stb
 
@@ -107,8 +107,8 @@
          industry, it is still uncommon in the videogame/real-time world.
 
          If you linearly filter non-premultiplied alpha, strange effects
-         occur. (For example, the average of 1% opaque bright green
-         and 99% opaque black produces 50% transparent dark green when
+         occur. (For example, the 50/50 average of 99% transparent bright green
+         and 1% transparent black produces 50% transparent dark green when
          non-premultiplied, whereas premultiplied it produces 50%
          transparent near-black. The former introduces green energy
          that doesn't exist in the source image.)
@@ -152,16 +152,20 @@
          (For example, graphics hardware does not apply sRGB conversion
          to the alpha channel.)
 
-   ADDITIONAL CONTRIBUTORS
+   CONTRIBUTORS
+      Jorge L Rodriguez: Implementation
       Sean Barrett: API design, optimizations
+      Aras Pranckevicius: bugfix
          
    REVISIONS
+      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
-      This software is in the public domain. Where that dedication is not
-      recognized, you are granted a perpetual, irrevocable license to copy
-      and modify this file as you see fit.
+     See end of file for license information.
 
    TODO
       Don't decode all of the image data when only processing a partial tile
@@ -382,15 +386,6 @@ STBIRDEF int stbir_resize_region(  const void *input_pixels , int input_w , int
 #define STBIR_ASSERT(x) assert(x)
 #endif
 
-#ifdef STBIR_DEBUG
-#define STBIR__DEBUG_ASSERT STBIR_ASSERT
-#else
-#define STBIR__DEBUG_ASSERT
-#endif
-
-// If you hit this it means I haven't done it yet.
-#define STBIR__UNIMPLEMENTED(x) STBIR_ASSERT(!(x))
-
 // For memset
 #include <string.h>
 
@@ -538,10 +533,11 @@ typedef struct
     int horizontal_num_contributors;
     int vertical_num_contributors;
 
-    int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter)
+    int ring_buffer_length_bytes;   // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter)
+    int ring_buffer_num_entries;    // Total number of entries in the ring buffer.
     int ring_buffer_first_scanline;
     int ring_buffer_last_scanline;
-    int ring_buffer_begin_index;
+    int ring_buffer_begin_index;    // first_scanline is at this index in the ring buffer
     float* ring_buffer;
 
     float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds.
@@ -556,16 +552,17 @@ typedef struct
     int encode_buffer_size;
 } stbir__info;
 
+
+static const float stbir__max_uint8_as_float  = 255.0f;
+static const float stbir__max_uint16_as_float = 65535.0f;
+static const double stbir__max_uint32_as_float = 4294967295.0;
+
+
 static stbir__inline int stbir__min(int a, int b)
 {
     return a < b ? a : b;
 }
 
-static stbir__inline int stbir__max(int a, int b)
-{
-    return a > b ? a : b;
-}
-
 static stbir__inline float stbir__saturate(float x)
 {
     if (x < 0)
@@ -757,7 +754,7 @@ static float stbir__filter_trapezoid(float x, float scale)
 {
     float halfscale = scale / 2;
     float t = 0.5f + halfscale;
-    STBIR__DEBUG_ASSERT(scale <= 1);
+    STBIR_ASSERT(scale <= 1);
 
     x = (float)fabs(x);
 
@@ -775,7 +772,7 @@ static float stbir__filter_trapezoid(float x, float scale)
 
 static float stbir__support_trapezoid(float scale)
 {
-    STBIR__DEBUG_ASSERT(scale <= 1);
+    STBIR_ASSERT(scale <= 1);
     return 0.5f + scale / 2;
 }
 
@@ -989,7 +986,7 @@ static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max)
         return n;  // NOTREACHED
 
     default:
-        STBIR__UNIMPLEMENTED("Unimplemented edge type");
+        STBIR_ASSERT(!"Unimplemented edge type");
         return 0;
     }
 }
@@ -1032,18 +1029,18 @@ static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radi
     *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5));
 }
 
-static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group)
+static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group)
 {
     int i;
     float total_filter = 0;
     float filter_scale;
 
-    STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
+    STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
 
     contributor->n0 = in_first_pixel;
     contributor->n1 = in_last_pixel;
 
-    STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0);
+    STBIR_ASSERT(contributor->n1 >= contributor->n0);
 
     for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
     {
@@ -1061,10 +1058,10 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi
         total_filter += coefficient_group[i];
     }
 
-    STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0);
+    STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0);
 
-    STBIR__DEBUG_ASSERT(total_filter > 0.9);
-    STBIR__DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off.
+    STBIR_ASSERT(total_filter > 0.9);
+    STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off.
 
     // Make sure the sum of all coefficients is 1.
     filter_scale = 1 / total_filter;
@@ -1082,16 +1079,16 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi
     }
 }
 
-static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group)
+static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group)
 {
     int i;
 
-     STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
+     STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
 
     contributor->n0 = out_first_pixel;
     contributor->n1 = out_last_pixel;
 
-    STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0);
+    STBIR_ASSERT(contributor->n1 >= contributor->n0);
 
     for (i = 0; i <= out_last_pixel - out_first_pixel; i++)
     {
@@ -1100,7 +1097,7 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st
         coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio;
     }
 
-    STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0);
+    STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0);
 
     for (i = out_last_pixel - out_first_pixel; i >= 0; i--)
     {
@@ -1112,7 +1109,7 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st
     }
 }
 
-static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size)
+static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size)
 {
     int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size);
     int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio);
@@ -1135,8 +1132,8 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, st
                 break;
         }
 
-        STBIR__DEBUG_ASSERT(total > 0.9f);
-        STBIR__DEBUG_ASSERT(total < 1.1f);
+        STBIR_ASSERT(total > 0.9f);
+        STBIR_ASSERT(total < 1.1f);
 
         scale = 1 / total;
 
@@ -1189,7 +1186,7 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, st
 
 // Each scan line uses the same kernel values so we should calculate the kernel
 // values once and then we can use them for every scan line.
-static void stbir__calculate_filters(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size)
+static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size)
 {
     int n;
     int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size);
@@ -1206,7 +1203,7 @@ static void stbir__calculate_filters(stbir__info* stbir_info, stbir__contributor
 
             stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out);
 
-            stbir__calculate_coefficients_upsample(stbir_info, filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
+            stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
         }
     }
     else
@@ -1222,10 +1219,10 @@ static void stbir__calculate_filters(stbir__info* stbir_info, stbir__contributor
 
             stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in);
 
-            stbir__calculate_coefficients_downsample(stbir_info, filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
+            stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0));
         }
 
-        stbir__normalize_downsample_coefficients(stbir_info, contributors, coefficients, filter, scale_ratio, shift, input_size, output_size);
+        stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size);
     }
 }
 
@@ -1246,11 +1243,11 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
     int type = stbir_info->type;
     int colorspace = stbir_info->colorspace;
     int input_w = stbir_info->input_w;
-    int input_stride_bytes = stbir_info->input_stride_bytes;
+    size_t input_stride_bytes = stbir_info->input_stride_bytes;
     float* decode_buffer = stbir__get_decode_buffer(stbir_info);
     stbir_edge edge_horizontal = stbir_info->edge_horizontal;
     stbir_edge edge_vertical = stbir_info->edge_vertical;
-    int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes;
+    size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes;
     const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset;
     int max_x = input_w + stbir_info->horizontal_filter_pixel_margin;
     int decode = STBIR__DECODE(type, colorspace);
@@ -1275,7 +1272,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
             int decode_pixel_index = x * channels;
             int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
             for (c = 0; c < channels; c++)
-                decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255;
+                decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float;
         }
         break;
 
@@ -1288,7 +1285,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
                 decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]];
 
             if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
-                decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255;
+                decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float;
         }
         break;
 
@@ -1298,7 +1295,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
             int decode_pixel_index = x * channels;
             int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
             for (c = 0; c < channels; c++)
-                decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535;
+                decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float;
         }
         break;
 
@@ -1308,10 +1305,10 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
             int decode_pixel_index = x * channels;
             int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
             for (c = 0; c < channels; c++)
-                decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535);
+                decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float);
 
             if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
-                decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535;
+                decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float;
         }
         break;
 
@@ -1321,7 +1318,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
             int decode_pixel_index = x * channels;
             int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
             for (c = 0; c < channels; c++)
-                decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295);
+                decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float);
         }
         break;
 
@@ -1331,10 +1328,10 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
             int decode_pixel_index = x * channels;
             int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels;
             for (c = 0; c < channels; c++)
-                decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295));
+                decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float));
 
             if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
-                decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295);
+                decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float);
         }
         break;
 
@@ -1363,7 +1360,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
         break;
 
     default:
-        STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination.");
+        STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
         break;
     }
 
@@ -1416,6 +1413,8 @@ static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n)
     int ring_buffer_index;
     float* ring_buffer;
 
+    stbir_info->ring_buffer_last_scanline = n;
+
     if (stbir_info->ring_buffer_begin_index < 0)
     {
         ring_buffer_index = stbir_info->ring_buffer_begin_index = 0;
@@ -1423,24 +1422,21 @@ static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n)
     }
     else
     {
-        ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir_info->vertical_filter_pixel_width;
-        STBIR__DEBUG_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index);
+        ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries;
+        STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index);
     }
 
     ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float));
     memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes);
 
-    stbir_info->ring_buffer_last_scanline = n;
-
     return ring_buffer;
 }
 
 
-static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, float* output_buffer)
+static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer)
 {
     int x, k;
     int output_w = stbir_info->output_w;
-    int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width;
     int channels = stbir_info->channels;
     float* decode_buffer = stbir__get_decode_buffer(stbir_info);
     stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors;
@@ -1456,11 +1452,11 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
         int coefficient_group = coefficient_width * x;
         int coefficient_counter = 0;
 
-        STBIR__DEBUG_ASSERT(n1 >= n0);
-        STBIR__DEBUG_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin);
-        STBIR__DEBUG_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin);
-        STBIR__DEBUG_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
-        STBIR__DEBUG_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
+        STBIR_ASSERT(n1 >= n0);
+        STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin);
+        STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin);
+        STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
+        STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
 
         switch (channels) {
             case 1:
@@ -1468,7 +1464,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
                 {
                     int in_pixel_index = k * 1;
                     float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
                 }
                 break;
@@ -1477,7 +1473,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
                 {
                     int in_pixel_index = k * 2;
                     float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
                     output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
                 }
@@ -1487,7 +1483,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
                 {
                     int in_pixel_index = k * 3;
                     float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
                     output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
                     output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
@@ -1498,7 +1494,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
                 {
                     int in_pixel_index = k * 4;
                     float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
                     output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
                     output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
@@ -1511,7 +1507,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
                     int in_pixel_index = k * channels;
                     float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
                     int c;
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     for (c = 0; c < channels; c++)
                         output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
                 }
@@ -1520,12 +1516,10 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
     }
 }
 
-static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n, float* output_buffer)
+static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer)
 {
     int x, k;
     int input_w = stbir_info->input_w;
-    int output_w = stbir_info->output_w;
-    int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width;
     int channels = stbir_info->channels;
     float* decode_buffer = stbir__get_decode_buffer(stbir_info);
     stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors;
@@ -1534,7 +1528,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
     int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin;
     int max_x = input_w + filter_pixel_margin * 2;
 
-    STBIR__DEBUG_ASSERT(!stbir__use_width_upsampling(stbir_info));
+    STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info));
 
     switch (channels) {
         case 1:
@@ -1552,7 +1546,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
                 {
                     int out_pixel_index = k * 1;
                     float coefficient = horizontal_coefficients[coefficient_group + k - n0];
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
                 }
             }
@@ -1573,7 +1567,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
                 {
                     int out_pixel_index = k * 2;
                     float coefficient = horizontal_coefficients[coefficient_group + k - n0];
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
                     output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
                 }
@@ -1595,7 +1589,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
                 {
                     int out_pixel_index = k * 3;
                     float coefficient = horizontal_coefficients[coefficient_group + k - n0];
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
                     output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
                     output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
@@ -1618,7 +1612,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
                 {
                     int out_pixel_index = k * 4;
                     float coefficient = horizontal_coefficients[coefficient_group + k - n0];
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
                     output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
                     output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
@@ -1643,7 +1637,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
                     int c;
                     int out_pixel_index = k * channels;
                     float coefficient = horizontal_coefficients[coefficient_group + k - n0];
-                    STBIR__DEBUG_ASSERT(coefficient != 0);
+                    STBIR_ASSERT(coefficient != 0);
                     for (c = 0; c < channels; c++)
                         output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
                 }
@@ -1659,9 +1653,9 @@ static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n)
 
     // Now resample it into the ring buffer.
     if (stbir__use_width_upsampling(stbir_info))
-        stbir__resample_horizontal_upsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n));
+        stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n));
     else
-        stbir__resample_horizontal_downsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n));
+        stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n));
 
     // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling.
 }
@@ -1675,17 +1669,17 @@ static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n
 
     // Now resample it into the horizontal buffer.
     if (stbir__use_width_upsampling(stbir_info))
-        stbir__resample_horizontal_upsample(stbir_info, n, stbir_info->horizontal_buffer);
+        stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer);
     else
-        stbir__resample_horizontal_downsample(stbir_info, n, stbir_info->horizontal_buffer);
+        stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer);
 
     // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers.
 }
 
 // Get the specified scan line from the ring buffer.
-static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length)
+static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length)
 {
-    int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size;
+    int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries;
     return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length);
 }
 
@@ -1720,19 +1714,23 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
 
     // build a table of all channels that need colorspace correction, so
     // we don't perform colorspace correction on channels that don't need it.
-    for (x=0, num_nonalpha=0; x < channels; ++x)
+    for (x = 0, num_nonalpha = 0; x < channels; ++x)
+    {
         if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
-            nonalpha[num_nonalpha++] = x;
+        {
+            nonalpha[num_nonalpha++] = (stbir_uint16)x;
+        }
+    }
 
     #define STBIR__ROUND_INT(f)    ((int)          ((f)+0.5))
     #define STBIR__ROUND_UINT(f)   ((stbir_uint32) ((f)+0.5))
 
     #ifdef STBIR__SATURATE_INT
-    #define STBIR__ENCODE_LINEAR8(f)   stbir__saturate8 (STBIR__ROUND_INT((f) * 255  ))
-    #define STBIR__ENCODE_LINEAR16(f)  stbir__saturate16(STBIR__ROUND_INT((f) * 65535))
+    #define STBIR__ENCODE_LINEAR8(f)   stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float ))
+    #define STBIR__ENCODE_LINEAR16(f)  stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float))
     #else
-    #define STBIR__ENCODE_LINEAR8(f)   (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * 255  )
-    #define STBIR__ENCODE_LINEAR16(f)  (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * 65535)
+    #define STBIR__ENCODE_LINEAR8(f)   (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float )
+    #define STBIR__ENCODE_LINEAR16(f)  (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float)
     #endif
 
     switch (decode)
@@ -1787,7 +1785,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
                 for (n = 0; n < num_nonalpha; n++)
                 {
                     int index = pixel_index + nonalpha[n];
-                    ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535);
+                    ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float);
                 }
 
                 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
@@ -1804,7 +1802,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
                 for (n = 0; n < channels; n++)
                 {
                     int index = pixel_index + n;
-                    ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * 4294967295);
+                    ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float);
                 }
             }
             break;
@@ -1817,11 +1815,11 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
                 for (n = 0; n < num_nonalpha; n++)
                 {
                     int index = pixel_index + nonalpha[n];
-                    ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295);
+                    ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float);
                 }
 
                 if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
-                    ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295);
+                    ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float);
             }
             break;
 
@@ -1855,12 +1853,12 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
             break;
 
         default:
-            STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination.");
+            STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
             break;
     }
 }
 
-static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out)
+static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n)
 {
     int x, k;
     int output_w = stbir_info->output_w;
@@ -1870,7 +1868,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
     int alpha_channel = stbir_info->alpha_channel;
     int type = stbir_info->type;
     int colorspace = stbir_info->colorspace;
-    int kernel_pixel_width = stbir_info->vertical_filter_pixel_width;
+    int ring_buffer_entries = stbir_info->ring_buffer_num_entries;
     void* output_data = stbir_info->output_data;
     float* encode_buffer = stbir_info->encode_buffer;
     int decode = STBIR__DECODE(type, colorspace);
@@ -1881,7 +1879,6 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
     float* ring_buffer = stbir_info->ring_buffer;
     int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index;
     int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline;
-    int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline;
     int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
 
     int n0,n1, output_row_start;
@@ -1892,7 +1889,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
 
     output_row_start = n * stbir_info->output_stride_bytes;
 
-    STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info));
+    STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
 
     memset(encode_buffer, 0, output_w * sizeof(float) * channels);
 
@@ -1905,7 +1902,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
             for (k = n0; k <= n1; k++)
             {
                 int coefficient_index = coefficient_counter++;
-                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length);
+                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
                 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
                 for (x = 0; x < output_w; ++x)
                 {
@@ -1918,7 +1915,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
             for (k = n0; k <= n1; k++)
             {
                 int coefficient_index = coefficient_counter++;
-                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length);
+                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
                 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
                 for (x = 0; x < output_w; ++x)
                 {
@@ -1932,7 +1929,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
             for (k = n0; k <= n1; k++)
             {
                 int coefficient_index = coefficient_counter++;
-                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length);
+                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
                 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
                 for (x = 0; x < output_w; ++x)
                 {
@@ -1947,7 +1944,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
             for (k = n0; k <= n1; k++)
             {
                 int coefficient_index = coefficient_counter++;
-                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length);
+                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
                 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
                 for (x = 0; x < output_w; ++x)
                 {
@@ -1963,7 +1960,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
             for (k = n0; k <= n1; k++)
             {
                 int coefficient_index = coefficient_counter++;
-                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length);
+                float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
                 float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
                 for (x = 0; x < output_w; ++x)
                 {
@@ -1978,16 +1975,14 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
     stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode);
 }
 
-static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out)
+static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n)
 {
     int x, k;
     int output_w = stbir_info->output_w;
-    int output_h = stbir_info->output_h;
     stbir__contributors* vertical_contributors = stbir_info->vertical_contributors;
     float* vertical_coefficients = stbir_info->vertical_coefficients;
     int channels = stbir_info->channels;
-    int kernel_pixel_width = stbir_info->vertical_filter_pixel_width;
-    void* output_data = stbir_info->output_data;
+    int ring_buffer_entries = stbir_info->ring_buffer_num_entries;
     float* horizontal_buffer = stbir_info->horizontal_buffer;
     int coefficient_width = stbir_info->vertical_coefficient_width;
     int contributor = n + stbir_info->vertical_filter_pixel_margin;
@@ -1995,14 +1990,13 @@ static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n,
     float* ring_buffer = stbir_info->ring_buffer;
     int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index;
     int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline;
-    int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline;
     int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float);
     int n0,n1;
 
     n0 = vertical_contributors[contributor].n0;
     n1 = vertical_contributors[contributor].n1;
 
-    STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info));
+    STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
 
     for (k = n0; k <= n1; k++)
     {
@@ -2010,7 +2004,7 @@ static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n,
         int coefficient_group = coefficient_width * contributor;
         float coefficient = vertical_coefficients[coefficient_group + coefficient_index];
 
-        float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length);
+        float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length);
 
         switch (channels) {
             case 1:
@@ -2067,7 +2061,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
     float scale_ratio = stbir_info->vertical_scale;
     float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio;
 
-    STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info));
+    STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
 
     for (y = 0; y < stbir_info->output_h; y++)
     {
@@ -2076,7 +2070,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
 
         stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out);
 
-        STBIR__DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbir_info->vertical_filter_pixel_width);
+        STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries);
 
         if (stbir_info->ring_buffer_begin_index >= 0)
         {
@@ -2095,7 +2089,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
                 else
                 {
                     stbir_info->ring_buffer_first_scanline++;
-                    stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width;
+                    stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries;
                 }
             }
         }
@@ -2108,7 +2102,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
             stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1);
 
         // Now all buffers should be ready to write a row of vertical sampling.
-        stbir__resample_vertical_upsample(stbir_info, y, in_first_scanline, in_last_scanline, in_center_of_out);
+        stbir__resample_vertical_upsample(stbir_info, y);
 
         STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h);
     }
@@ -2153,7 +2147,7 @@ static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessar
             else
             {
                 stbir_info->ring_buffer_first_scanline++;
-                stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width;
+                stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries;
             }
         }
     }
@@ -2168,7 +2162,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
     int pixel_margin = stbir_info->vertical_filter_pixel_margin;
     int max_y = stbir_info->input_h + pixel_margin;
 
-    STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info));
+    STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
 
     for (y = -pixel_margin; y < max_y; y++)
     {
@@ -2177,7 +2171,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
 
         stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in);
 
-        STBIR__DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbir_info->vertical_filter_pixel_width);
+        STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries);
 
         if (out_last_scanline < 0 || out_first_scanline >= output_h)
             continue;
@@ -2194,7 +2188,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
             stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1);
 
         // Now the horizontal buffer is ready to write to all ring buffer rows.
-        stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in);
+        stbir__resample_vertical_downsample(stbir_info, y);
     }
 
     stbir__empty_ring_buffer(stbir_info, stbir_info->output_h);
@@ -2228,8 +2222,8 @@ static void stbir__calculate_transform(stbir__info *info, float s0, float t0, fl
         info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0);
         info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0);
 
-        info->horizontal_shift = s0 * info->input_w / (s1 - s0);
-        info->vertical_shift = t0 * info->input_h / (t1 - t0);
+        info->horizontal_shift = s0 * info->output_w / (s1 - s0);
+        info->vertical_shift = t0 * info->output_h / (t1 - t0);
     }
 }
 
@@ -2251,13 +2245,16 @@ static stbir_uint32 stbir__calculate_memory(stbir__info *info)
     info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w);
     info->vertical_num_contributors   = stbir__get_contributors(info->vertical_scale  , info->vertical_filter  , info->input_h, info->output_h);
 
+    // One extra entry because floating point precision problems sometimes cause an extra to be necessary.
+    info->ring_buffer_num_entries = filter_height + 1;
+
     info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors);
     info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float);
     info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors);
     info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float);
     info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float);
     info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float);
-    info->ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float);
+    info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float);
     info->encode_buffer_size = info->output_w * info->channels * sizeof(float);
 
     STBIR_ASSERT(info->horizontal_filter != 0);
@@ -2379,7 +2376,7 @@ static int stbir__resize_allocated(stbir__info *info,
         info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float);
         info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float);
 
-        STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
+        STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
     }
     else
     {
@@ -2387,7 +2384,7 @@ static int stbir__resize_allocated(stbir__info *info,
         info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float);
         info->encode_buffer = NULL;
 
-        STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
+        STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
     }
 
 #undef STBIR__NEXT_MEMPTR
@@ -2395,8 +2392,8 @@ static int stbir__resize_allocated(stbir__info *info,
     // This signals that the ring buffer is empty
     info->ring_buffer_begin_index = -1;
 
-    stbir__calculate_filters(info, info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w);
-    stbir__calculate_filters(info, info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h);
+    stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w);
+    stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h);
 
     STBIR_PROGRESS_REPORT(0);
 
@@ -2408,10 +2405,10 @@ static int stbir__resize_allocated(stbir__info *info,
     STBIR_PROGRESS_REPORT(1);
 
 #ifdef STBIR_DEBUG_OVERWRITE_TEST
-    STBIR__DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
-    STBIR__DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0);
-    STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
-    STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0);
+    STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
+    STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0);
+    STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
+    STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0);
 #endif
 
     return 1;
@@ -2583,3 +2580,45 @@ STBIRDEF int stbir_resize_region(  const void *input_pixels , int input_w , int
 }
 
 #endif // STB_IMAGE_RESIZE_IMPLEMENTATION
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
+software, either in source code form or as a compiled binary, for any purpose, 
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this 
+software dedicate any and all copyright interest in the software to the public 
+domain. We make this dedication for the benefit of the public at large and to 
+the detriment of our heirs and successors. We intend this dedication to be an 
+overt act of relinquishment in perpetuity of all present and future rights to 
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/

+ 497 - 135
stb_image_write.h

@@ -1,7 +1,6 @@
-/* stb_image_write - v0.98 - public domain - http://nothings.org/stb/stb_image_write.h
-   writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010
-                            no warranty implied; use at your own risk
-
+/* stb_image_write - v1.05 - public domain - http://nothings.org/stb/stb_image_write.h
+   writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015
+                                     no warranty implied; use at your own risk
 
    Before #including,
 
@@ -18,7 +17,7 @@ ABOUT:
 
    The PNG output is not optimal; it is 20-50% larger than the file
    written by a decent optimizing implementation. This library is designed
-   for source code compactness and simplicitly, not optimal image file size
+   for source code compactness and simplicity, not optimal image file size
    or run-time performance.
 
 BUILDING:
@@ -35,7 +34,22 @@ USAGE:
      int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
      int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
      int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
-     int stbi_write_hdr(char const *filename, int w, int h, int comp, const void *data);
+     int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
+
+   There are also four equivalent functions that use an arbitrary write function. You are
+   expected to open/close your file-equivalent before and after calling these:
+
+     int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data, int stride_in_bytes);
+     int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);
+     int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);
+     int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
+
+   where the callback is:
+      void stbi_write_func(void *context, void *data, int size);
+
+   You can define STBI_WRITE_NO_STDIO to disable the file variant of these
+   functions, so the library will not use stdio.h at all. However, this will
+   also disable HDR writing, because it requires stdio for formatted output.
 
    Each function returns 0 on failure and non-0 on success.
 
@@ -63,6 +77,9 @@ USAGE:
    data, alpha (if provided) is discarded, and for monochrome data it is
    replicated across all three channels.
 
+   TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed
+   data, set the global variable 'stbi_write_tga_with_rle' to 0.
+
 CREDITS:
 
    PNG/BMP/TGA
@@ -73,8 +90,26 @@ CREDITS:
       Jean-Sebastien Guay
    misc enhancements:
       Tim Kelsey
+   TGA RLE
+      Alan Hickman
+   initial file IO callback implementation
+      Emmanuel Julien
    bugfixes:
       github:Chribba
+      Guillaume Chereau
+      github:jry2
+      github:romigrou
+      Sergio Gonzalez
+      Jonas Karlsson
+      Filip Wasil
+      Thatcher Ulrich
+      github:poppolopoppo
+      Patrick Boettcher
+      
+LICENSE
+
+  See end of file for license information.
+
 */
 
 #ifndef INCLUDE_STB_IMAGE_WRITE_H
@@ -84,10 +119,26 @@ CREDITS:
 extern "C" {
 #endif
 
-extern int stbi_write_png(char const *filename, int w, int h, int comp, const void  *data, int stride_in_bytes);
-extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void  *data);
-extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void  *data);
-extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
+#ifdef STB_IMAGE_WRITE_STATIC
+#define STBIWDEF static
+#else
+#define STBIWDEF extern
+extern int stbi_write_tga_with_rle;
+#endif
+
+#ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void  *data, int stride_in_bytes);
+STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void  *data);
+STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void  *data);
+STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
+#endif
+
+typedef void stbi_write_func(void *context, void *data, int size);
+
+STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data, int stride_in_bytes);
+STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);
+STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);
+STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
 
 #ifdef __cplusplus
 }
@@ -97,25 +148,43 @@ extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const fl
 
 #ifdef STB_IMAGE_WRITE_IMPLEMENTATION
 
+#ifdef _WIN32
+   #ifndef _CRT_SECURE_NO_WARNINGS
+   #define _CRT_SECURE_NO_WARNINGS
+   #endif
+   #ifndef _CRT_NONSTDC_NO_DEPRECATE
+   #define _CRT_NONSTDC_NO_DEPRECATE
+   #endif
+#endif
+
+#ifndef STBI_WRITE_NO_STDIO
+#include <stdio.h>
+#endif // STBI_WRITE_NO_STDIO
+
 #include <stdarg.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
 #include <math.h>
 
-#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC)
+#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))
 // ok
-#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC)
+#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)
 // ok
 #else
-#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC."
+#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)."
 #endif
 
 #ifndef STBIW_MALLOC
-#define STBIW_MALLOC(sz)    malloc(sz)
-#define STBIW_REALLOC(p,sz) realloc(p,sz)
-#define STBIW_FREE(p)       free(p)
+#define STBIW_MALLOC(sz)        malloc(sz)
+#define STBIW_REALLOC(p,newsz)  realloc(p,newsz)
+#define STBIW_FREE(p)           free(p)
+#endif
+
+#ifndef STBIW_REALLOC_SIZED
+#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)
 #endif
+
+
 #ifndef STBIW_MEMMOVE
 #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)
 #endif
@@ -126,22 +195,73 @@ extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const fl
 #define STBIW_ASSERT(x) assert(x)
 #endif
 
+#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)
+
+typedef struct
+{
+   stbi_write_func *func;
+   void *context;
+} stbi__write_context;
+
+// initialize a callback-based context
+static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context)
+{
+   s->func    = c;
+   s->context = context;
+}
+
+#ifndef STBI_WRITE_NO_STDIO
+
+static void stbi__stdio_write(void *context, void *data, int size)
+{
+   fwrite(data,1,size,(FILE*) context);
+}
+
+static int stbi__start_write_file(stbi__write_context *s, const char *filename)
+{
+   FILE *f = fopen(filename, "wb");
+   stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);
+   return f != NULL;
+}
+
+static void stbi__end_write_file(stbi__write_context *s)
+{
+   fclose((FILE *)s->context);
+}
+
+#endif // !STBI_WRITE_NO_STDIO
+
 typedef unsigned int stbiw_uint32;
 typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
 
-static void writefv(FILE *f, const char *fmt, va_list v)
+#ifdef STB_IMAGE_WRITE_STATIC
+static int stbi_write_tga_with_rle = 1;
+#else
+int stbi_write_tga_with_rle = 1;
+#endif
+
+static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)
 {
    while (*fmt) {
       switch (*fmt++) {
          case ' ': break;
-         case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; }
-         case '2': { int x = va_arg(v,int); unsigned char b[2];
-                     b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8);
-                     fwrite(b,2,1,f); break; }
-         case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4];
-                     b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8);
-                     b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);
-                     fwrite(b,4,1,f); break; }
+         case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));
+                     s->func(s->context,&x,1);
+                     break; }
+         case '2': { int x = va_arg(v,int);
+                     unsigned char b[2];
+                     b[0] = STBIW_UCHAR(x);
+                     b[1] = STBIW_UCHAR(x>>8);
+                     s->func(s->context,b,2);
+                     break; }
+         case '4': { stbiw_uint32 x = va_arg(v,int);
+                     unsigned char b[4];
+                     b[0]=STBIW_UCHAR(x);
+                     b[1]=STBIW_UCHAR(x>>8);
+                     b[2]=STBIW_UCHAR(x>>16);
+                     b[3]=STBIW_UCHAR(x>>24);
+                     s->func(s->context,b,4);
+                     break; }
          default:
             STBIW_ASSERT(0);
             return;
@@ -149,18 +269,58 @@ static void writefv(FILE *f, const char *fmt, va_list v)
    }
 }
 
-static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c)
+static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
+{
+   va_list v;
+   va_start(v, fmt);
+   stbiw__writefv(s, fmt, v);
+   va_end(v);
+}
+
+static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)
 {
    unsigned char arr[3];
    arr[0] = a, arr[1] = b, arr[2] = c;
-   fwrite(arr, 3, 1, f);
+   s->func(s->context, arr, 3);
 }
 
-static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)
+static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)
 {
    unsigned char bg[3] = { 255, 0, 255}, px[3];
+   int k;
+
+   if (write_alpha < 0)
+      s->func(s->context, &d[comp - 1], 1);
+
+   switch (comp) {
+      case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case
+      case 1:
+         if (expand_mono)
+            stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
+         else
+            s->func(s->context, d, 1);  // monochrome TGA
+         break;
+      case 4:
+         if (!write_alpha) {
+            // composite against pink background
+            for (k = 0; k < 3; ++k)
+               px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
+            stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
+            break;
+         }
+         /* FALLTHROUGH */
+      case 3:
+         stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
+         break;
+   }
+   if (write_alpha > 0)
+      s->func(s->context, &d[comp - 1], 1);
+}
+
+static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)
+{
    stbiw_uint32 zero = 0;
-   int i,j,k, j_end;
+   int i,j, j_end;
 
    if (y <= 0)
       return;
@@ -173,73 +333,147 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp,
    for (; j != j_end; j += vdir) {
       for (i=0; i < x; ++i) {
          unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
-         if (write_alpha < 0)
-            fwrite(&d[comp-1], 1, 1, f);
-         switch (comp) {
-            case 1: fwrite(d, 1, 1, f);
-                    break;
-            case 2: if (expand_mono)
-                       write3(f, d[0],d[0],d[0]); // monochrome bmp
-                    else
-                       fwrite(d, 1, 1, f);  // monochrome TGA
-                    break;
-            case 4:
-               if (!write_alpha) {
-                  // composite against pink background
-                  for (k=0; k < 3; ++k)
-                     px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
-                  write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]);
-                  break;
-               }
-               /* FALLTHROUGH */
-            case 3:
-               write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]);
-               break;
-         }
-         if (write_alpha > 0)
-            fwrite(&d[comp-1], 1, 1, f);
+         stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
       }
-      fwrite(&zero,scanline_pad,1,f);
+      s->func(s->context, &zero, scanline_pad);
    }
 }
 
-static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)
+static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)
 {
-   FILE *f;
-   if (y < 0 || x < 0) return 0;
-   f = fopen(filename, "wb");
-   if (f) {
+   if (y < 0 || x < 0) {
+      return 0;
+   } else {
       va_list v;
       va_start(v, fmt);
-      writefv(f, fmt, v);
+      stbiw__writefv(s, fmt, v);
       va_end(v);
-      write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad,expand_mono);
-      fclose(f);
+      stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);
+      return 1;
    }
-   return f != NULL;
 }
 
-int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
+static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)
 {
    int pad = (-x*3) & 3;
-   return outfile(filename,-1,-1,x,y,comp,1,(void *) data,0,pad,
+   return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,
            "11 4 22 4" "4 44 22 444444",
            'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40,  // file header
             40, x,y, 1,24, 0,0,0,0,0,0);             // bitmap header
 }
 
-int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
+STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
+{
+   stbi__write_context s;
+   stbi__start_write_callbacks(&s, func, context);
+   return stbi_write_bmp_core(&s, x, y, comp, data);
+}
+
+#ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
+{
+   stbi__write_context s;
+   if (stbi__start_write_file(&s,filename)) {
+      int r = stbi_write_bmp_core(&s, x, y, comp, data);
+      stbi__end_write_file(&s);
+      return r;
+   } else
+      return 0;
+}
+#endif //!STBI_WRITE_NO_STDIO
+
+static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data)
 {
    int has_alpha = (comp == 2 || comp == 4);
    int colorbytes = has_alpha ? comp-1 : comp;
    int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
-   return outfile(filename, -1,-1, x, y, comp, 0, (void *) data, has_alpha, 0,
-                  "111 221 2222 11", 0,0,format, 0,0,0, 0,0,x,y, (colorbytes+has_alpha)*8, has_alpha*8);
+
+   if (y < 0 || x < 0)
+      return 0;
+
+   if (!stbi_write_tga_with_rle) {
+      return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,
+         "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
+   } else {
+      int i,j,k;
+
+      stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);
+
+      for (j = y - 1; j >= 0; --j) {
+          unsigned char *row = (unsigned char *) data + j * x * comp;
+         int len;
+
+         for (i = 0; i < x; i += len) {
+            unsigned char *begin = row + i * comp;
+            int diff = 1;
+            len = 1;
+
+            if (i < x - 1) {
+               ++len;
+               diff = memcmp(begin, row + (i + 1) * comp, comp);
+               if (diff) {
+                  const unsigned char *prev = begin;
+                  for (k = i + 2; k < x && len < 128; ++k) {
+                     if (memcmp(prev, row + k * comp, comp)) {
+                        prev += comp;
+                        ++len;
+                     } else {
+                        --len;
+                        break;
+                     }
+                  }
+               } else {
+                  for (k = i + 2; k < x && len < 128; ++k) {
+                     if (!memcmp(begin, row + k * comp, comp)) {
+                        ++len;
+                     } else {
+                        break;
+                     }
+                  }
+               }
+            }
+
+            if (diff) {
+               unsigned char header = STBIW_UCHAR(len - 1);
+               s->func(s->context, &header, 1);
+               for (k = 0; k < len; ++k) {
+                  stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
+               }
+            } else {
+               unsigned char header = STBIW_UCHAR(len - 129);
+               s->func(s->context, &header, 1);
+               stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
+            }
+         }
+      }
+   }
+   return 1;
+}
+
+int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
+{
+   stbi__write_context s;
+   stbi__start_write_callbacks(&s, func, context);
+   return stbi_write_tga_core(&s, x, y, comp, (void *) data);
 }
 
+#ifndef STBI_WRITE_NO_STDIO
+int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
+{
+   stbi__write_context s;
+   if (stbi__start_write_file(&s,filename)) {
+      int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);
+      stbi__end_write_file(&s);
+      return r;
+   } else
+      return 0;
+}
+#endif
+
 // *************************************************************************************************
 // Radiance RGBE HDR writer
 // by Baldur Karlsson
+
 #define stbiw__max(a, b)  ((a) > (b) ? (a) : (b))
 
 void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
@@ -247,7 +481,7 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
    int exponent;
    float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));
 
-   if (maxcomp < 1e-32) {
+   if (maxcomp < 1e-32f) {
       rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
    } else {
       float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;
@@ -259,23 +493,23 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
    }
 }
 
-void stbiw__write_run_data(FILE *f, int length, unsigned char databyte)
+void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)
 {
-   unsigned char lengthbyte = (unsigned char) (length+128);
+   unsigned char lengthbyte = STBIW_UCHAR(length+128);
    STBIW_ASSERT(length+128 <= 255);
-   fwrite(&lengthbyte, 1, 1, f);
-   fwrite(&databyte, 1, 1, f);
+   s->func(s->context, &lengthbyte, 1);
+   s->func(s->context, &databyte, 1);
 }
 
-void stbiw__write_dump_data(FILE *f, int length, unsigned char *data)
+void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)
 {
-   unsigned char lengthbyte = (unsigned char )(length & 0xff);
+   unsigned char lengthbyte = STBIW_UCHAR(length);
    STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
-   fwrite(&lengthbyte, 1, 1, f);
-   fwrite(data, length, 1, f);
+   s->func(s->context, &lengthbyte, 1);
+   s->func(s->context, data, length);
 }
 
-void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scratch, const float *scanline)
+void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)
 {
    unsigned char scanlineheader[4] = { 2, 2, 0, 0 };
    unsigned char rgbe[4];
@@ -288,31 +522,31 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
    /* skip RLE for images too small or large */
    if (width < 8 || width >= 32768) {
       for (x=0; x < width; x++) {
-         switch (comp) {
+         switch (ncomp) {
             case 4: /* fallthrough */
-            case 3: linear[2] = scanline[x*comp + 2];
-                    linear[1] = scanline[x*comp + 1];
-                    linear[0] = scanline[x*comp + 0];
+            case 3: linear[2] = scanline[x*ncomp + 2];
+                    linear[1] = scanline[x*ncomp + 1];
+                    linear[0] = scanline[x*ncomp + 0];
                     break;
-            case 2: /* fallthrough */
-            case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0];
+            default:
+                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
                     break;
          }
          stbiw__linear_to_rgbe(rgbe, linear);
-         fwrite(rgbe, 4, 1, f);
+         s->func(s->context, rgbe, 4);
       }
    } else {
       int c,r;
       /* encode into scratch buffer */
       for (x=0; x < width; x++) {
-         switch(comp) {
+         switch(ncomp) {
             case 4: /* fallthrough */
-            case 3: linear[2] = scanline[x*comp + 2];
-                    linear[1] = scanline[x*comp + 1];
-                    linear[0] = scanline[x*comp + 0];
+            case 3: linear[2] = scanline[x*ncomp + 2];
+                    linear[1] = scanline[x*ncomp + 1];
+                    linear[0] = scanline[x*ncomp + 0];
                     break;
-            case 2: /* fallthrough */
-            case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0];
+            default:
+                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
                     break;
          }
          stbiw__linear_to_rgbe(rgbe, linear);
@@ -322,7 +556,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
          scratch[x + width*3] = rgbe[3];
       }
 
-      fwrite(scanlineheader, 4, 1, f);
+      s->func(s->context, scanlineheader, 4);
 
       /* RLE each component separately */
       for (c=0; c < 4; c++) {
@@ -343,7 +577,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
             while (x < r) {
                int len = r-x;
                if (len > 128) len = 128;
-               stbiw__write_dump_data(f, len, &comp[x]);
+               stbiw__write_dump_data(s, len, &comp[x]);
                x += len;
             }
             // if there's a run, output it
@@ -355,7 +589,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
                while (x < r) {
                   int len = r-x;
                   if (len > 127) len = 127;
-                  stbiw__write_run_data(f, len, comp[x]);
+                  stbiw__write_run_data(s, len, comp[x]);
                   x += len;
                }
             }
@@ -364,27 +598,53 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
    }
 }
 
-int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
+static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)
 {
-   int i;
-   FILE *f;
-   if (y <= 0 || x <= 0 || data == NULL) return 0;
-   f = fopen(filename, "wb");
-   if (f) {
-      /* Each component is stored separately. Allocate scratch space for full output scanline. */
+   if (y <= 0 || x <= 0 || data == NULL)
+      return 0;
+   else {
+      // Each component is stored separately. Allocate scratch space for full output scanline.
       unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
-      fprintf(f, "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"      );
-      fprintf(f, "EXPOSURE=          1.0000000000000\n\n-Y %d +X %d\n"                 , y, x);
+      int i, len;
+      char buffer[128];
+      char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
+      s->func(s->context, header, sizeof(header)-1);
+
+      len = sprintf(buffer, "EXPOSURE=          1.0000000000000\n\n-Y %d +X %d\n", y, x);
+      s->func(s->context, buffer, len);
+
       for(i=0; i < y; i++)
-         stbiw__write_hdr_scanline(f, x, comp, scratch, data + comp*i*x);
+         stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x);
       STBIW_FREE(scratch);
-      fclose(f);
+      return 1;
    }
-   return f != NULL;
 }
 
-/////////////////////////////////////////////////////////
-// PNG
+int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)
+{
+   stbi__write_context s;
+   stbi__start_write_callbacks(&s, func, context);
+   return stbi_write_hdr_core(&s, x, y, comp, (float *) data);
+}
+
+#ifndef STBI_WRITE_NO_STDIO
+int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
+{
+   stbi__write_context s;
+   if (stbi__start_write_file(&s,filename)) {
+      int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);
+      stbi__end_write_file(&s);
+      return r;
+   } else
+      return 0;
+}
+#endif // STBI_WRITE_NO_STDIO
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PNG writer
+//
 
 // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
 #define stbiw__sbraw(a) ((int *) (a) - 2)
@@ -402,7 +662,7 @@ int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *da
 static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
 {
    int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;
-   void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
+   void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);
    STBIW_ASSERT(p);
    if (p) {
       if (!*arr) ((int *) p)[1] = 0;
@@ -415,7 +675,7 @@ static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
 static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
 {
    while (*bitcount >= 8) {
-      stbiw__sbpush(data, (unsigned char) *bitbuffer);
+      stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));
       *bitbuffer >>= 8;
       *bitcount -= 8;
    }
@@ -475,7 +735,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
    unsigned int bitbuf=0;
    int i,j, bitcount=0;
    unsigned char *out = NULL;
-   unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack!
+   unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**));
    if (quality < 5) quality = 5;
 
    stbiw__sbpush(out, 0x78);   // DEFLATE 32K window
@@ -547,21 +807,23 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
 
    for (i=0; i < stbiw__ZHASH; ++i)
       (void) stbiw__sbfree(hash_table[i]);
+   STBIW_FREE(hash_table);
 
    {
       // compute adler32 on input
-      unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552;
-      int j=0;
+      unsigned int s1=1, s2=0;
+      int blocklen = (int) (data_len % 5552);
+      j=0;
       while (j < data_len) {
          for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;
          s1 %= 65521, s2 %= 65521;
          j += blocklen;
          blocklen = 5552;
       }
-      stbiw__sbpush(out, (unsigned char) (s2 >> 8));
-      stbiw__sbpush(out, (unsigned char) s2);
-      stbiw__sbpush(out, (unsigned char) (s1 >> 8));
-      stbiw__sbpush(out, (unsigned char) s1);
+      stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));
+      stbiw__sbpush(out, STBIW_UCHAR(s2));
+      stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));
+      stbiw__sbpush(out, STBIW_UCHAR(s1));
    }
    *out_len = stbiw__sbn(out);
    // make returned pointer freeable
@@ -569,21 +831,52 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
    return (unsigned char *) stbiw__sbraw(out);
 }
 
-unsigned int stbiw__crc32(unsigned char *buffer, int len)
+static unsigned int stbiw__crc32(unsigned char *buffer, int len)
 {
-   static unsigned int crc_table[256];
+   static unsigned int crc_table[256] =
+   {
+      0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+      0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+      0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+      0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+      0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+      0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+      0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+      0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+      0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+      0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+      0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+      0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+      0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+      0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+      0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+      0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+      0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+      0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+      0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+      0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+      0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+      0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+      0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+      0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+      0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+      0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+      0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+      0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+      0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+      0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+      0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+      0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+   };
+
    unsigned int crc = ~0u;
-   int i,j;
-   if (crc_table[1] == 0)
-      for(i=0; i < 256; i++)
-         for (crc_table[i]=i, j=0; j < 8; ++j)
-            crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0);
+   int i;
    for (i=0; i < len; ++i)
       crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
    return ~crc;
 }
 
-#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4)
+#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4)
 #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
 #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])
 
@@ -596,11 +889,12 @@ static void stbiw__wpcrc(unsigned char **data, int len)
 static unsigned char stbiw__paeth(int a, int b, int c)
 {
    int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
-   if (pa <= pb && pa <= pc) return (unsigned char) a;
-   if (pb <= pc) return (unsigned char) b;
-   return (unsigned char) c;
+   if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);
+   if (pb <= pc) return STBIW_UCHAR(b);
+   return STBIW_UCHAR(c);
 }
 
+// @OPTIMIZE: provide an option that always forces left-predict or paeth predict
 unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
 {
    int ctype[5] = { -1, 0, 4, 2, 6 };
@@ -617,10 +911,10 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in
    for (j=0; j < y; ++j) {
       static int mapping[] = { 0,1,2,3,4 };
       static int firstmap[] = { 0,1,0,5,6 };
-      int *mymap = j ? mapping : firstmap;
+      int *mymap = (j != 0) ? mapping : firstmap;
       int best = 0, bestval = 0x7fffffff;
       for (p=0; p < 2; ++p) {
-         for (k= p?best:0; k < 5; ++k) {
+         for (k= p?best:0; k < 5; ++k) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass
             int type = mymap[k],est=0;
             unsigned char *z = pixels + stride_bytes*j;
             for (i=0; i < n; ++i)
@@ -671,7 +965,7 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in
    stbiw__wp32(o, x);
    stbiw__wp32(o, y);
    *o++ = 8;
-   *o++ = (unsigned char) ctype[n];
+   *o++ = STBIW_UCHAR(ctype[n]);
    *o++ = 0;
    *o++ = 0;
    *o++ = 0;
@@ -693,12 +987,13 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in
    return out;
 }
 
-int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
+#ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
 {
    FILE *f;
    int len;
    unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
-   if (!png) return 0;
+   if (png == NULL) return 0;
    f = fopen(filename, "wb");
    if (!f) { STBIW_FREE(png); return 0; }
    fwrite(png, 1, len, f);
@@ -706,9 +1001,34 @@ int stbi_write_png(char const *filename, int x, int y, int comp, const void *dat
    STBIW_FREE(png);
    return 1;
 }
+#endif
+
+STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)
+{
+   int len;
+   unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
+   if (png == NULL) return 0;
+   func(context, png, len);
+   STBIW_FREE(png);
+   return 1;
+}
+
 #endif // STB_IMAGE_WRITE_IMPLEMENTATION
 
 /* Revision history
+      1.04 (2017-03-03)
+             monochrome BMP expansion
+      1.03   ???
+      1.02 (2016-04-02)
+             avoid allocating large structures on the stack
+      1.01 (2016-01-16)
+             STBIW_REALLOC_SIZED: support allocators with no realloc support
+             avoid race-condition in crc initialization
+             minor compile issues
+      1.00 (2015-09-14)
+             installable file IO function
+      0.99 (2015-09-13)
+             warning fixes; TGA rle support
       0.98 (2015-04-08)
              added STBIW_MALLOC, STBIW_ASSERT etc
       0.97 (2015-01-18)
@@ -728,3 +1048,45 @@ int stbi_write_png(char const *filename, int x, int y, int comp, const void *dat
              first public release
       0.90   first internal release
 */
+
+/*
+------------------------------------------------------------------------------
+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.
+------------------------------------------------------------------------------
+*/

+ 52 - 4
stb_leakcheck.h

@@ -1,7 +1,10 @@
-// stb_leakcheck.h - v0.2 - quick & dirty malloc leak-checking - public domain
+// stb_leakcheck.h - v0.3 - quick & dirty malloc leak-checking - public domain
+// LICENSE
+//
+//   See end of file.
 
 #ifdef STB_LEAKCHECK_IMPLEMENTATION
-#undef STB_LEAKCHECK_IMPLEMENTATION // don't implenment more than once
+#undef STB_LEAKCHECK_IMPLEMENTATION // don't implement more than once
 
 // if we've already included leakcheck before, undefine the macros
 #ifdef malloc
@@ -10,6 +13,8 @@
 #undef realloc
 #endif
 
+#include <assert.h>
+#include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stddef.h>
@@ -88,14 +93,14 @@ void stb_leakcheck_dumpmem(void)
    stb_leakcheck_malloc_info *mi = mi_head;
    while (mi) {
       if ((ptrdiff_t) mi->size >= 0)
-         printf("LEAKED: %s (%4d): %8z bytes at %p\n", mi->file, mi->line, mi->size, mi+1);
+         printf("LEAKED: %s (%4d): %8d bytes at %p\n", mi->file, mi->line, (int) mi->size, mi+1);
       mi = mi->next;
    }
    #ifdef STB_LEAKCHECK_SHOWALL
    mi = mi_head;
    while (mi) {
       if ((ptrdiff_t) mi->size < 0)
-         printf("FREED : %s (%4d): %8z bytes at %p\n", mi->file, mi->line, ~mi->size, mi+1);
+         printf("FREED : %s (%4d): %8d bytes at %p\n", mi->file, mi->line, (int) ~mi->size, mi+1);
       mi = mi->next;
    }
    #endif
@@ -115,3 +120,46 @@ extern void   stb_leakcheck_free(void *ptr);
 extern void   stb_leakcheck_dumpmem(void);
 
 #endif // INCLUDE_STB_LEAKCHECK_H
+
+
+/*
+------------------------------------------------------------------------------
+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.
+------------------------------------------------------------------------------
+*/

+ 153 - 12
stb_perlin.h

@@ -1,11 +1,16 @@
-// stb_perlin.h - v0.2 - perlin noise
+// stb_perlin.h - v0.3 - perlin noise
 // public domain single-file C implementation by Sean Barrett
 //
+// LICENSE
+//
+//   See end of file.
+//
+//
 // to create the implementation,
 //     #define STB_PERLIN_IMPLEMENTATION
 // in *one* C/CPP file that includes this file.
-
-
+//
+//
 // Documentation:
 //
 // float  stb_perlin_noise3( float x,
@@ -26,22 +31,55 @@
 // 0 to mean "don't care". (The noise always wraps every 256 due
 // details of the implementation, even if you ask for larger or no
 // wrapping.)
+//
+// Fractal Noise:
+//
+// Three common fractal noise functions are included, which produce 
+// a wide variety of nice effects depending on the parameters 
+// provided. Note that each function will call stb_perlin_noise3 
+// 'octaves' times, so this parameter will affect runtime.
+//
+// float stb_perlin_ridge_noise3(float x, float y, float z,
+//                               float lacunarity, float gain, float offset, int octaves,
+//                               int x_wrap, int y_wrap, int z_wrap);
+//
+// float stb_perlin_fbm_noise3(float x, float y, float z,
+//                             float lacunarity, float gain, int octaves,
+//                             int x_wrap, int y_wrap, int z_wrap);
+//
+// float stb_perlin_turbulence_noise3(float x, float y, float z,
+//                                    float lacunarity, float gain,int octaves,
+//                                    int x_wrap, int y_wrap, int z_wrap);
+//
+// Typical values to start playing with:
+//     octaves    =   6     -- number of "octaves" of noise3() to sum
+//     lacunarity = ~ 2.0   -- spacing between successive octaves (use exactly 2.0 for wrapping output)
+//     gain       =   0.5   -- relative weighting applied to each successive octave
+//     offset     =   1.0?  -- used to invert the ridges, may need to be larger, not sure
+//    
+//
+// Contributors:
+//    Jack Mott - additional noise functions
+//
 
 
 #ifdef __cplusplus
-extern "C" float stb_perlin_noise3(float x, float y, float z, int x_wrap=0, int y_wrap=0, int z_wrap=0);
-#else
+extern "C" {
+#endif
 extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap);
+extern float stb_perlin_ridge_noise3(float x, float y, float z,float lacunarity, float gain, float offset, int octaves,int x_wrap, int y_wrap, int z_wrap);
+extern float stb_perlin_fbm_noise3(float x, float y, float z,float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap);
+extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap);
+#ifdef __cplusplus
+}
 #endif
 
 #ifdef STB_PERLIN_IMPLEMENTATION
 
-#include <math.h> // floor()
-
 // not same permutation table as Perlin's reference to avoid copyright issues;
 // Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/
 // @OPTIMIZE: should this be unsigned char instead of int for cache?
-static int stb__perlin_randtab[512] =
+static unsigned char stb__perlin_randtab[512] =
 {
    23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, 
    152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, 
@@ -84,6 +122,12 @@ static float stb__perlin_lerp(float a, float b, float t)
    return a + (b-a) * t;
 }
 
+static int stb__perlin_fastfloor(float a)
+{
+	int ai = (int) a;
+	return (a < ai) ? ai-1 : ai;
+}
+
 // different grad function from Perlin's, but easy to modify to match reference
 static float stb__perlin_grad(int hash, float x, float y, float z)
 {
@@ -105,7 +149,7 @@ static float stb__perlin_grad(int hash, float x, float y, float z)
 
    // perlin's gradient has 12 cases so some get used 1/16th of the time
    // and some 2/16ths. We reduce bias by changing those fractions
-   // to 5/16ths and 6/16ths, and the same 4 cases get the extra weight.
+   // to 5/64ths and 6/64ths, and the same 4 cases get the extra weight.
    static unsigned char indices[64] =
    {
       0,1,2,3,4,5,6,7,8,9,10,11,
@@ -117,6 +161,7 @@ static float stb__perlin_grad(int hash, float x, float y, float z)
    };
 
    // if you use reference permutation table, change 63 below to 15 to match reference
+   // (this is why the ordering of the table above is funky)
    float *grad = basis[indices[hash & 63]];
    return grad[0]*x + grad[1]*y + grad[2]*z;
 }
@@ -131,9 +176,9 @@ float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z
    unsigned int x_mask = (x_wrap-1) & 255;
    unsigned int y_mask = (y_wrap-1) & 255;
    unsigned int z_mask = (z_wrap-1) & 255;
-   int px = (int) floor(x);
-   int py = (int) floor(y);
-   int pz = (int) floor(z);
+   int px = stb__perlin_fastfloor(x);
+   int py = stb__perlin_fastfloor(y);
+   int pz = stb__perlin_fastfloor(z);
    int x0 = px & x_mask, x1 = (px+1) & x_mask;
    int y0 = py & y_mask, y1 = (py+1) & y_mask;
    int z0 = pz & z_mask, z1 = (pz+1) & z_mask;
@@ -172,4 +217,100 @@ float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z
 
    return stb__perlin_lerp(n0,n1,u);
 }
+
+float stb_perlin_ridge_noise3(float x, float y, float z,float lacunarity, float gain, float offset, int octaves,int x_wrap, int y_wrap, int z_wrap)
+{
+   int i;
+   float frequency = 1.0f;
+   float prev = 1.0f;
+   float amplitude = 0.5f;
+   float sum = 0.0f;
+
+   for (i = 0; i < octaves; i++) {
+      float r = (float)(stb_perlin_noise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap));
+      r = r<0 ? -r : r; // fabs()
+      r = offset - r;
+      r = r*r;
+      sum += r*amplitude*prev;
+      prev = r;
+      frequency *= lacunarity;
+      amplitude *= gain;
+   }
+   return sum;
+}
+
+float stb_perlin_fbm_noise3(float x, float y, float z,float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap)
+{
+   int i;
+   float frequency = 1.0f;
+   float amplitude = 1.0f;
+   float sum = 0.0f;
+   
+   for (i = 0; i < octaves; i++) {
+      sum += stb_perlin_noise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap)*amplitude;
+      frequency *= lacunarity;
+      amplitude *= gain;
+   }
+   return sum;
+}
+
+float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap)
+{
+   int i;
+   float frequency = 1.0f;
+   float amplitude = 1.0f;
+   float sum = 0.0f;
+   
+   for (i = 0; i < octaves; i++) {
+      float r = stb_perlin_noise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap)*amplitude;
+      r = r<0 ? -r : r; // fabs()
+      sum += r;
+      frequency *= lacunarity;
+      amplitude *= gain;
+   }
+   return sum;
+}
+
 #endif  // STB_PERLIN_IMPLEMENTATION
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
+software, either in source code form or as a compiled binary, for any purpose, 
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this 
+software dedicate any and all copyright interest in the software to the public 
+domain. We make this dedication for the benefit of the public at large and to 
+the detriment of our heirs and successors. We intend this dedication to be an 
+overt act of relinquishment in perpetuity of all present and future rights to 
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/

+ 95 - 20
stb_rect_pack.h

@@ -1,4 +1,4 @@
-// stb_rect_pack.h - v0.06 - public domain - rectangle packing
+// stb_rect_pack.h - v0.11 - public domain - rectangle packing
 // Sean Barrett 2014
 //
 // Useful for e.g. packing rectangular textures into an atlas.
@@ -27,15 +27,26 @@
 //    Sean Barrett
 //  Minor features
 //    Martins Mozeiko
+//    github:IntellectualKitty
+//    
 //  Bugfixes / warning fixes
-//    [your name could be here]
+//    Jeremy Jaussaud
 //
 // Version history:
 //
+//     0.11  (2017-03-03)  return packing success/fail result
+//     0.10  (2016-10-25)  remove cast-away-const to avoid warnings
+//     0.09  (2016-08-27)  fix compiler warnings
+//     0.08  (2015-09-13)  really fix bug with empty rects (w=0 or h=0)
+//     0.07  (2015-09-13)  fix bug with empty rects (w=0 or h=0)
 //     0.06  (2015-04-15)  added STBRP_SORT to allow replacing qsort
 //     0.05:  added STBRP_ASSERT to allow replacing assert
 //     0.04:  fixed minor bug in STBRP_LARGE_RECTS support
 //     0.01:  initial release
+//
+// LICENSE
+//
+//   See end of file for license information.
 
 //////////////////////////////////////////////////////////////////////////////
 //
@@ -67,7 +78,7 @@ typedef int            stbrp_coord;
 typedef unsigned short stbrp_coord;
 #endif
 
-STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
+STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
 // Assign packed locations to rectangles. The rectangles are of type
 // 'stbrp_rect' defined below, stored in the array 'rects', and there
 // are 'num_rects' many of them.
@@ -88,6 +99,9 @@ STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int
 // arrays will probably produce worse packing results than calling it
 // a single time with the full rectangle array, but the option is
 // available.
+//
+// The function returns 1 if all of the rectangles were successfully
+// packed and 0 otherwise.
 
 struct stbrp_rect
 {
@@ -140,7 +154,7 @@ enum
 {
    STBRP_HEURISTIC_Skyline_default=0,
    STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
-   STBRP_HEURISTIC_Skyline_BF_sortHeight,
+   STBRP_HEURISTIC_Skyline_BF_sortHeight
 };
 
 
@@ -190,9 +204,15 @@ struct stbrp_context
 #define STBRP_ASSERT assert
 #endif
 
+#ifdef _MSC_VER
+#define STBRP__NOTUSED(v)  (void)(v)
+#else
+#define STBRP__NOTUSED(v)  (void)sizeof(v)
+#endif
+
 enum
 {
-   STBRP__INIT_skyline = 1,
+   STBRP__INIT_skyline = 1
 };
 
 STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
@@ -265,6 +285,9 @@ static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0
    stbrp_node *node = first;
    int x1 = x0 + width;
    int min_y, visited_width, waste_area;
+
+   STBRP__NOTUSED(c);
+
    STBRP_ASSERT(first->x <= x0);
 
    #if 0
@@ -492,8 +515,8 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
 
 static int rect_height_compare(const void *a, const void *b)
 {
-   stbrp_rect *p = (stbrp_rect *) a;
-   stbrp_rect *q = (stbrp_rect *) b;
+   const stbrp_rect *p = (const stbrp_rect *) a;
+   const stbrp_rect *q = (const stbrp_rect *) b;
    if (p->h > q->h)
       return -1;
    if (p->h < q->h)
@@ -503,8 +526,8 @@ static int rect_height_compare(const void *a, const void *b)
 
 static int rect_width_compare(const void *a, const void *b)
 {
-   stbrp_rect *p = (stbrp_rect *) a;
-   stbrp_rect *q = (stbrp_rect *) b;
+   const stbrp_rect *p = (const stbrp_rect *) a;
+   const stbrp_rect *q = (const stbrp_rect *) b;
    if (p->w > q->w)
       return -1;
    if (p->w < q->w)
@@ -514,8 +537,8 @@ static int rect_width_compare(const void *a, const void *b)
 
 static int rect_original_order(const void *a, const void *b)
 {
-   stbrp_rect *p = (stbrp_rect *) a;
-   stbrp_rect *q = (stbrp_rect *) b;
+   const stbrp_rect *p = (const stbrp_rect *) a;
+   const stbrp_rect *q = (const stbrp_rect *) b;
    return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
 }
 
@@ -525,9 +548,9 @@ static int rect_original_order(const void *a, const void *b)
 #define STBRP__MAXVAL  0xffff
 #endif
 
-STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
+STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
 {
-   int i;
+   int i, all_rects_packed = 1;
 
    // we use the 'was_packed' field internally to allow sorting/unsorting
    for (i=0; i < num_rects; ++i) {
@@ -541,20 +564,72 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n
    STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
 
    for (i=0; i < num_rects; ++i) {
-      stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
-      if (fr.prev_link) {
-         rects[i].x = (stbrp_coord) fr.x;
-         rects[i].y = (stbrp_coord) fr.y;
+      if (rects[i].w == 0 || rects[i].h == 0) {
+         rects[i].x = rects[i].y = 0;  // empty rect needs no space
       } else {
-         rects[i].x = rects[i].y = STBRP__MAXVAL;
+         stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
+         if (fr.prev_link) {
+            rects[i].x = (stbrp_coord) fr.x;
+            rects[i].y = (stbrp_coord) fr.y;
+         } else {
+            rects[i].x = rects[i].y = STBRP__MAXVAL;
+         }
       }
    }
 
    // unsort
    STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
 
-   // set was_packed flags
-   for (i=0; i < num_rects; ++i)
+   // set was_packed flags and all_rects_packed status
+   for (i=0; i < num_rects; ++i) {
       rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
+      if (!rects[i].was_packed)
+         all_rects_packed = 0;
+   }
+
+   // return the all_rects_packed status
+   return all_rects_packed;
 }
 #endif
+
+/*
+------------------------------------------------------------------------------
+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.
+------------------------------------------------------------------------------
+*/

+ 1202 - 0
stb_sprintf.h

@@ -0,0 +1,1202 @@
+// stb_sprintf - v1.02 - public domain snprintf() implementation
+// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
+// http://github.com/nothings/stb
+//
+// allowed types:  sc uidBboXx p AaGgEef n
+// lengths      :  h ll j z t I64 I32 I
+//
+// Contributors (bugfixes):
+//    github:d26435
+//    github:trex78
+//    Jari Komppa (SI suffixes)
+//
+// LICENSE:
+//
+//   See end of file for license information.
+
+#ifndef STB_SPRINTF_H_INCLUDE
+#define STB_SPRINTF_H_INCLUDE
+
+/*
+Single file sprintf replacement.
+
+Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. 
+Hereby placed in public domain.
+
+This is a full sprintf replacement that supports everything that
+the C runtime sprintfs support, including float/double, 64-bit integers,
+hex floats, field parameters (%*.*d stuff), length reads backs, etc.
+
+Why would you need this if sprintf already exists?  Well, first off,
+it's *much* faster (see below). It's also much smaller than the CRT
+versions code-space-wise. We've also added some simple improvements 
+that are super handy (commas in thousands, callbacks at buffer full,
+for example). Finally, the format strings for MSVC and GCC differ 
+for 64-bit integers (among other small things), so this lets you use 
+the same format strings in cross platform code.
+
+It uses the standard single file trick of being both the header file
+and the source itself. If you just include it normally, you just get 
+the header file function definitions. To get the code, you include
+it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
+
+It only uses va_args macros from the C runtime to do it's work. It
+does cast doubles to S64s and shifts and divides U64s, which does 
+drag in CRT code on most platforms.
+
+It compiles to roughly 8K with float support, and 4K without.
+As a comparison, when using MSVC static libs, calling sprintf drags
+in 16K.
+
+API:
+====
+int stbsp_sprintf( char * buf, char const * fmt, ... )
+int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
+  Convert an arg list into a buffer.  stbsp_snprintf always returns
+  a zero-terminated string (unlike regular snprintf).
+
+int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
+int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
+  Convert a va_list arg list into a buffer.  stbsp_vsnprintf always returns
+  a zero-terminated string (unlike regular snprintf).
+
+int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
+    typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
+  Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
+  Your callback can then copy the chars out, print them or whatever.
+  This function is actually the workhorse for everything else.
+  The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
+    // you return the next buffer to use or 0 to stop converting
+
+void stbsp_set_separators( char comma, char period )
+  Set the comma and period characters to use.
+
+FLOATS/DOUBLES:
+===============
+This code uses a internal float->ascii conversion method that uses
+doubles with error correction (double-doubles, for ~105 bits of
+precision).  This conversion is round-trip perfect - that is, an atof
+of the values output here will give you the bit-exact double back.
+
+One difference is that our insignificant digits will be different than 
+with MSVC or GCC (but they don't match each other either).  We also 
+don't attempt to find the minimum length matching float (pre-MSVC15 
+doesn't either).
+
+If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
+and you'll save 4K of code space.
+
+64-BIT INTS:
+============
+This library also supports 64-bit integers and you can use MSVC style or
+GCC style indicators (%I64d or %lld).  It supports the C99 specifiers
+for size_t and ptr_diff_t (%jd %zd) as well.
+
+EXTRAS:
+=======
+Like some GCCs, for integers and floats, you can use a ' (single quote)
+specifier and commas will be inserted on the thousands: "%'d" on 12345 
+would print 12,345.
+
+For integers and floats, you can use a "$" specifier and the number 
+will be converted to float and then divided to get kilo, mega, giga or
+tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is 
+"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn 
+2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
+$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
+suffix, add "_" specifier: "%_$d" -> "2.53M".
+
+In addition to octal and hexadecimal conversions, you can print 
+integers in binary: "%b" for 256 would print 100.
+
+PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
+===================================================================
+"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
+"%24d" across all 32-bit ints (4.5x/4.2x faster)
+"%x" across all 32-bit ints (4.5x/3.8x faster)
+"%08x" across all 32-bit ints (4.3x/3.8x faster)
+"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
+"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
+"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
+"%f" for values near e-300 (7.9x/6.5x faster)
+"%f" for values near e+300 (10.0x/9.1x faster)
+"%e" for values near e-300 (10.1x/7.0x faster)
+"%e" for values near e+300 (9.2x/6.0x faster)
+"%.320f" for values near e-300 (12.6x/11.2x faster)
+"%a" for random values (8.6x/4.3x faster)
+"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
+"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
+"%s%s%s" for 64 char strings (7.1x/7.3x faster)
+"...512 char string..." ( 35.0x/32.5x faster!)
+*/
+
+#if defined(__has_feature)
+ #if __has_feature(address_sanitizer)
+  #define STBI__ASAN __attribute__((no_sanitize("address")))
+ #endif
+#endif
+#ifndef STBI__ASAN
+#define STBI__ASAN
+#endif
+
+#ifdef STB_SPRINTF_STATIC
+#define STBSP__PUBLICDEC static
+#define STBSP__PUBLICDEF static STBI__ASAN
+#else
+#ifdef __cplusplus
+#define STBSP__PUBLICDEC extern "C"
+#define STBSP__PUBLICDEF extern "C" STBI__ASAN
+#else
+#define STBSP__PUBLICDEC extern
+#define STBSP__PUBLICDEF STBI__ASAN
+#endif
+#endif
+
+#include <stdarg.h>  // for va_list()
+
+#ifndef STB_SPRINTF_MIN
+#define STB_SPRINTF_MIN 512 // how many characters per callback
+#endif
+typedef char * STBSP_SPRINTFCB( char * buf, void * user, int len );
+
+#ifndef STB_SPRINTF_DECORATE
+#define STB_SPRINTF_DECORATE(name) stbsp_##name  // define this before including if you want to change the names
+#endif
+
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsprintf )( char * buf, char const * fmt, va_list va );
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va );
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( sprintf ) ( char * buf, char const * fmt, ... );
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( snprintf )( char * buf, int count, char const * fmt, ... );
+
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsprintfcb )( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va );
+STBSP__PUBLICDEF void STB_SPRINTF_DECORATE( set_separators )( char comma, char period );
+
+#endif // STB_SPRINTF_H_INCLUDE
+
+#ifdef STB_SPRINTF_IMPLEMENTATION
+
+#include <stdlib.h>  // for va_arg()
+
+#define stbsp__uint32 unsigned int
+#define stbsp__int32 signed int
+
+#ifdef _MSC_VER
+#define stbsp__uint64 unsigned __int64
+#define stbsp__int64 signed __int64
+#else
+#define stbsp__uint64 unsigned long long
+#define stbsp__int64 signed long long
+#endif
+#define stbsp__uint16 unsigned short
+
+#ifndef stbsp__uintptr 
+#if defined(__ppc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
+#define stbsp__uintptr stbsp__uint64
+#else
+#define stbsp__uintptr stbsp__uint32
+#endif
+#endif
+
+#ifndef STB_SPRINTF_MSVC_MODE  // used for MSVC2013 and earlier (MSVC2015 matches GCC)
+#if defined(_MSC_VER) && (_MSC_VER<1900)
+#define STB_SPRINTF_MSVC_MODE
+#endif
+#endif
+
+#ifdef STB_SPRINTF_NOUNALIGNED  // define this before inclusion to force stbsp_sprintf to always use aligned accesses
+#define STBSP__UNALIGNED(code)
+#else
+#define STBSP__UNALIGNED(code) code
+#endif
+
+#ifndef STB_SPRINTF_NOFLOAT
+// internal float utility functions
+static stbsp__int32 stbsp__real_to_str( char const * * start, stbsp__uint32 * len, char *out, stbsp__int32 * decimal_pos, double value, stbsp__uint32 frac_digits );
+static stbsp__int32 stbsp__real_to_parts( stbsp__int64 * bits, stbsp__int32 * expo, double value );
+#define STBSP__SPECIAL 0x7000
+#endif
+
+static char stbsp__period='.';
+static char stbsp__comma=',';
+static char stbsp__digitpair[201]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
+
+STBSP__PUBLICDEF void STB_SPRINTF_DECORATE( set_separators )( char pcomma, char pperiod )
+{
+  stbsp__period=pperiod;
+  stbsp__comma=pcomma;
+}
+
+#define STBSP__LEFTJUST 1
+#define STBSP__LEADINGPLUS 2
+#define STBSP__LEADINGSPACE 4
+#define STBSP__LEADING_0X 8
+#define STBSP__LEADINGZERO 16
+#define STBSP__INTMAX 32
+#define STBSP__TRIPLET_COMMA 64
+#define STBSP__NEGATIVE 128
+#define STBSP__METRIC_SUFFIX 256
+#define STBSP__HALFWIDTH 512
+#define STBSP__METRIC_NOSPACE 1024
+#define STBSP__METRIC_1024 2048
+#define STBSP__METRIC_JEDEC 4096
+
+static void stbsp__lead_sign(stbsp__uint32 fl, char *sign)
+{
+  sign[0] = 0;
+  if (fl&STBSP__NEGATIVE) {
+    sign[0]=1;
+    sign[1]='-';
+  } else if (fl&STBSP__LEADINGSPACE) {
+    sign[0]=1;
+    sign[1]=' ';
+  } else if (fl&STBSP__LEADINGPLUS) {
+    sign[0]=1;
+    sign[1]='+';
+  }
+}
+  
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsprintfcb )( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
+{
+  static char hex[]="0123456789abcdefxp";
+  static char hexu[]="0123456789ABCDEFXP";
+  char * bf;
+  char const * f;
+  int tlen = 0;
+
+  bf = buf;
+  f = fmt;
+  for(;;)
+  {
+    stbsp__int32 fw,pr,tz; stbsp__uint32 fl;
+
+    // macros for the callback buffer stuff
+    #define stbsp__chk_cb_bufL(bytes) { int len = (int)(bf-buf); if ((len+(bytes))>=STB_SPRINTF_MIN) { tlen+=len; if (0==(bf=buf=callback(buf,user,len))) goto done; } }
+    #define stbsp__chk_cb_buf(bytes) { if ( callback ) { stbsp__chk_cb_bufL(bytes); } }
+    #define stbsp__flush_cb() { stbsp__chk_cb_bufL(STB_SPRINTF_MIN-1); } //flush if there is even one byte in the buffer
+    #define stbsp__cb_buf_clamp(cl,v) cl = v; if ( callback ) { int lg = STB_SPRINTF_MIN-(int)(bf-buf); if (cl>lg) cl=lg; }
+
+    // fast copy everything up to the next % (or end of string)
+    for(;;)
+    { 
+      while (((stbsp__uintptr)f)&3)
+      {
+       schk1: if (f[0]=='%') goto scandd;
+       schk2: if (f[0]==0) goto endfmt;
+        stbsp__chk_cb_buf(1); *bf++=f[0]; ++f;
+      } 
+      for(;;)
+      { 
+        // Check if the next 4 bytes contain %(0x25) or end of string.
+        // Using the 'hasless' trick:
+        // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
+        stbsp__uint32 v,c;
+        v=*(stbsp__uint32*)f; c=(~v)&0x80808080;
+        if (((v^0x25252525)-0x01010101)&c) goto schk1;
+        if ((v-0x01010101)&c) goto schk2; 
+        if (callback) if ((STB_SPRINTF_MIN-(int)(bf-buf))<4) goto schk1;
+        *(stbsp__uint32*)bf=v; bf+=4; f+=4;
+      }
+    } scandd:
+
+    ++f;
+
+    // ok, we have a percent, read the modifiers first
+    fw = 0; pr = -1; fl = 0; tz = 0;
+    
+    // flags
+    for(;;)
+    {
+      switch(f[0])
+      {
+        // if we have left justify
+        case '-': fl|=STBSP__LEFTJUST; ++f; continue;
+        // if we have leading plus
+        case '+': fl|=STBSP__LEADINGPLUS; ++f; continue; 
+        // if we have leading space
+        case ' ': fl|=STBSP__LEADINGSPACE; ++f; continue; 
+        // if we have leading 0x
+        case '#': fl|=STBSP__LEADING_0X; ++f; continue; 
+        // if we have thousand commas
+        case '\'': fl|=STBSP__TRIPLET_COMMA; ++f; continue; 
+        // if we have kilo marker (none->kilo->kibi->jedec)
+        case '$': 
+            if (fl&STBSP__METRIC_SUFFIX)
+            {
+                if (fl&STBSP__METRIC_1024)
+                {
+                    fl|=STBSP__METRIC_JEDEC;
+                }
+                else
+                {
+                    fl|=STBSP__METRIC_1024;
+                }
+            }
+            else
+            {
+                fl|=STBSP__METRIC_SUFFIX; 
+            }
+            ++f; continue; 
+        // if we don't want space between metric suffix and number
+        case '_': fl|=STBSP__METRIC_NOSPACE; ++f; continue;
+        // if we have leading zero
+        case '0': fl|=STBSP__LEADINGZERO; ++f; goto flags_done; 
+        default: goto flags_done;
+      }
+    }
+    flags_done:
+   
+    // get the field width
+    if ( f[0] == '*' ) {fw = va_arg(va,stbsp__uint32); ++f;} else { while (( f[0] >= '0' ) && ( f[0] <= '9' )) { fw = fw * 10 + f[0] - '0'; f++; } }
+    // get the precision
+    if ( f[0]=='.' ) { ++f; if ( f[0] == '*' ) {pr = va_arg(va,stbsp__uint32); ++f;} else { pr = 0; while (( f[0] >= '0' ) && ( f[0] <= '9' )) { pr = pr * 10 + f[0] - '0'; f++; } } } 
+    
+    // handle integer size overrides
+    switch(f[0])
+    {
+      // are we halfwidth?
+      case 'h': fl|=STBSP__HALFWIDTH; ++f; break;
+      // are we 64-bit (unix style)
+      case 'l': ++f; if ( f[0]=='l') { fl|=STBSP__INTMAX; ++f; } break;
+      // are we 64-bit on intmax? (c99)
+      case 'j': fl|=STBSP__INTMAX; ++f; break; 
+      // are we 64-bit on size_t or ptrdiff_t? (c99)
+      case 'z': case 't': fl|=((sizeof(char*)==8)?STBSP__INTMAX:0); ++f; break; 
+      // are we 64-bit (msft style)
+      case 'I': if ( ( f[1]=='6') && ( f[2]=='4') ) { fl|=STBSP__INTMAX; f+=3; }
+                else if ( ( f[1]=='3') && ( f[2]=='2') ) { f+=3; }
+                else { fl|=((sizeof(void*)==8)?STBSP__INTMAX:0); ++f; } break;
+      default: break;
+    }
+
+    // handle each replacement
+    switch( f[0] )
+    {
+      #define STBSP__NUMSZ   512 // big enough for e308 (with commas) or e-307 
+      char num[STBSP__NUMSZ]; 
+      char lead[8]; 
+      char tail[8]; 
+      char *s;
+      char const *h;
+      stbsp__uint32 l,n,cs;
+      stbsp__uint64 n64;
+      #ifndef STB_SPRINTF_NOFLOAT      
+      double fv; 
+      #endif
+      stbsp__int32 dp; char const * sn;
+
+      case 's':
+        // get the string
+        s = va_arg(va,char*); if (s==0) s = (char*)"null";
+        // get the length
+        sn = s;
+        for(;;)
+        { 
+          if ((((stbsp__uintptr)sn)&3)==0) break;
+         lchk:
+          if (sn[0]==0) goto ld;
+          ++sn;
+        }
+        n = 0xffffffff;
+        if (pr>=0) { n=(stbsp__uint32)(sn-s); if (n>=(stbsp__uint32)pr) goto ld; n=((stbsp__uint32)(pr-n))>>2; }
+        while(n) 
+        { 
+          stbsp__uint32 v=*(stbsp__uint32*)sn;
+          if ((v-0x01010101)&(~v)&0x80808080UL) goto lchk; 
+          sn+=4; 
+          --n;
+        }
+        goto lchk;
+       ld:
+
+        l = (stbsp__uint32) ( sn - s );
+        // clamp to precision
+        if ( l > (stbsp__uint32)pr ) l = pr;
+        lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
+        // copy the string in
+        goto scopy;
+
+      case 'c': // char
+        // get the character
+        s = num + STBSP__NUMSZ -1; *s = (char)va_arg(va,int);
+        l = 1;
+        lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
+        goto scopy;
+
+      case 'n': // weird write-bytes specifier
+        { int * d = va_arg(va,int*);
+        *d = tlen + (int)( bf - buf ); }
+        break;
+
+#ifdef STB_SPRINTF_NOFLOAT
+      case 'A': // float
+      case 'a': // hex float
+      case 'G': // float
+      case 'g': // float
+      case 'E': // float
+      case 'e': // float
+      case 'f': // float
+        va_arg(va,double); // eat it
+        s = (char*)"No float";
+        l = 8;
+        lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
+        goto scopy;
+#else
+      case 'A': // float
+        h=hexu;  
+        goto hexfloat;
+
+      case 'a': // hex float
+        h=hex;
+       hexfloat: 
+        fv = va_arg(va,double);
+        if (pr==-1) pr=6; // default is 6
+        // read the double into a string
+        if ( stbsp__real_to_parts( (stbsp__int64*)&n64, &dp, fv ) )
+          fl |= STBSP__NEGATIVE;
+  
+        s = num+64;
+
+        stbsp__lead_sign(fl, lead);
+
+        if (dp==-1023) dp=(n64)?-1022:0; else n64|=(((stbsp__uint64)1)<<52);
+        n64<<=(64-56);
+        if (pr<15) n64+=((((stbsp__uint64)8)<<56)>>(pr*4));
+        // add leading chars
+        
+        #ifdef STB_SPRINTF_MSVC_MODE
+        *s++='0';*s++='x';
+        #else
+        lead[1+lead[0]]='0'; lead[2+lead[0]]='x'; lead[0]+=2;
+        #endif
+        *s++=h[(n64>>60)&15]; n64<<=4;
+        if ( pr ) *s++=stbsp__period;
+        sn = s;
+
+        // print the bits
+        n = pr; if (n>13) n = 13; if (pr>(stbsp__int32)n) tz=pr-n; pr = 0;
+        while(n--) { *s++=h[(n64>>60)&15]; n64<<=4; }
+
+        // print the expo
+        tail[1]=h[17];
+        if (dp<0) { tail[2]='-'; dp=-dp;} else tail[2]='+';
+        n = (dp>=1000)?6:((dp>=100)?5:((dp>=10)?4:3));
+        tail[0]=(char)n;
+        for(;;) { tail[n]='0'+dp%10; if (n<=3) break; --n; dp/=10; }
+
+        dp = (int)(s-sn);
+        l = (int)(s-(num+64));
+        s = num+64;
+        cs = 1 + (3<<24);
+        goto scopy;
+
+      case 'G': // float
+        h=hexu;
+        goto dosmallfloat;
+
+      case 'g': // float
+        h=hex;
+       dosmallfloat:   
+        fv = va_arg(va,double);
+        if (pr==-1) pr=6; else if (pr==0) pr = 1; // default is 6
+        // read the double into a string
+        if ( stbsp__real_to_str( &sn, &l, num, &dp, fv, (pr-1)|0x80000000 ) )
+          fl |= STBSP__NEGATIVE;
+
+        // clamp the precision and delete extra zeros after clamp
+        n = pr;
+        if ( l > (stbsp__uint32)pr ) l = pr; while ((l>1)&&(pr)&&(sn[l-1]=='0')) { --pr; --l; }
+
+        // should we use %e
+        if ((dp<=-4)||(dp>(stbsp__int32)n))
+        {
+          if ( pr > (stbsp__int32)l ) pr = l-1; else if ( pr ) --pr; // when using %e, there is one digit before the decimal
+          goto doexpfromg;
+        }
+        // this is the insane action to get the pr to match %g sematics for %f
+        if(dp>0) { pr=(dp<(stbsp__int32)l)?l-dp:0; } else { pr = -dp+((pr>(stbsp__int32)l)?l:pr); }
+        goto dofloatfromg;
+
+      case 'E': // float
+        h=hexu;  
+        goto doexp;
+
+      case 'e': // float
+        h=hex;
+       doexp:   
+        fv = va_arg(va,double);
+        if (pr==-1) pr=6; // default is 6
+        // read the double into a string
+        if ( stbsp__real_to_str( &sn, &l, num, &dp, fv, pr|0x80000000 ) )
+          fl |= STBSP__NEGATIVE;
+       doexpfromg: 
+        tail[0]=0; 
+        stbsp__lead_sign(fl, lead);
+        if ( dp == STBSP__SPECIAL ) { s=(char*)sn; cs=0; pr=0; goto scopy; }
+        s=num+64; 
+        // handle leading chars
+        *s++=sn[0];
+
+        if (pr) *s++=stbsp__period;
+
+        // handle after decimal
+        if ((l-1)>(stbsp__uint32)pr) l=pr+1;
+        for(n=1;n<l;n++) *s++=sn[n];
+        // trailing zeros
+        tz = pr-(l-1); pr=0;
+        // dump expo
+        tail[1]=h[0xe];
+        dp -= 1;
+        if (dp<0) { tail[2]='-'; dp=-dp;} else tail[2]='+';
+        #ifdef STB_SPRINTF_MSVC_MODE
+        n = 5;
+        #else
+        n = (dp>=100)?5:4;
+        #endif
+        tail[0]=(char)n;
+        for(;;) { tail[n]='0'+dp%10; if (n<=3) break; --n; dp/=10; }
+        cs = 1 + (3<<24); // how many tens
+        goto flt_lead;   
+
+      case 'f': // float
+        fv = va_arg(va,double);
+       doafloat: 
+        // do kilos
+        if (fl&STBSP__METRIC_SUFFIX) 
+        {
+            double divisor;
+            divisor=1000.0f;
+            if (fl&STBSP__METRIC_1024) divisor = 1024.0;
+            while(fl<0x4000000) { if ((fv<divisor) && (fv>-divisor)) break; fv/=divisor; fl+=0x1000000; }
+        }
+        if (pr==-1) pr=6; // default is 6
+        // read the double into a string
+        if ( stbsp__real_to_str( &sn, &l, num, &dp, fv, pr ) )
+          fl |= STBSP__NEGATIVE;
+        dofloatfromg:
+        tail[0]=0;
+        stbsp__lead_sign(fl, lead);
+        if ( dp == STBSP__SPECIAL ) { s=(char*)sn; cs=0; pr=0; goto scopy; }
+        s=num+64; 
+
+        // handle the three decimal varieties
+        if (dp<=0) 
+        { 
+          stbsp__int32 i;
+          // handle 0.000*000xxxx
+          *s++='0'; if (pr) *s++=stbsp__period; 
+          n=-dp; if((stbsp__int32)n>pr) n=pr; i=n; while(i) { if ((((stbsp__uintptr)s)&3)==0) break; *s++='0'; --i; } while(i>=4) { *(stbsp__uint32*)s=0x30303030; s+=4; i-=4; } while(i) { *s++='0'; --i; }
+          if ((stbsp__int32)(l+n)>pr) l=pr-n; i=l; while(i) { *s++=*sn++; --i; }
+          tz = pr-(n+l);
+          cs = 1 + (3<<24); // how many tens did we write (for commas below)
+        }
+        else
+        {
+          cs = (fl&STBSP__TRIPLET_COMMA)?((600-(stbsp__uint32)dp)%3):0;
+          if ((stbsp__uint32)dp>=l)
+          {
+            // handle xxxx000*000.0
+            n=0; for(;;) { if ((fl&STBSP__TRIPLET_COMMA) && (++cs==4)) { cs = 0; *s++=stbsp__comma; } else { *s++=sn[n]; ++n; if (n>=l) break; } }
+            if (n<(stbsp__uint32)dp)
+            {
+              n = dp - n;
+              if ((fl&STBSP__TRIPLET_COMMA)==0) { while(n) { if ((((stbsp__uintptr)s)&3)==0) break; *s++='0'; --n; }  while(n>=4) { *(stbsp__uint32*)s=0x30303030; s+=4; n-=4; } }
+              while(n) { if ((fl&STBSP__TRIPLET_COMMA) && (++cs==4)) { cs = 0; *s++=stbsp__comma; } else { *s++='0'; --n; } }
+            }
+            cs = (int)(s-(num+64)) + (3<<24); // cs is how many tens
+            if (pr) { *s++=stbsp__period; tz=pr;}
+          }
+          else
+          {
+            // handle xxxxx.xxxx000*000
+            n=0; for(;;) { if ((fl&STBSP__TRIPLET_COMMA) && (++cs==4)) { cs = 0; *s++=stbsp__comma; } else { *s++=sn[n]; ++n; if (n>=(stbsp__uint32)dp) break; } }
+            cs = (int)(s-(num+64)) + (3<<24); // cs is how many tens
+            if (pr) *s++=stbsp__period;
+            if ((l-dp)>(stbsp__uint32)pr) l=pr+dp;
+            while(n<l) { *s++=sn[n]; ++n; }
+            tz = pr-(l-dp);
+          }
+        }
+        pr = 0;
+        
+        // handle k,m,g,t
+        if (fl&STBSP__METRIC_SUFFIX) 
+        { 
+            char idx;
+            idx=1;
+            if (fl&STBSP__METRIC_NOSPACE)
+                idx=0;
+            tail[0]=idx; 
+            tail[1]=' '; 
+            { 
+                if (fl>>24) 
+                {   // SI kilo is 'k', JEDEC and SI kibits are 'K'.
+                    if (fl&STBSP__METRIC_1024)
+                        tail[idx+1]="_KMGT"[fl>>24]; 
+                    else
+                        tail[idx+1]="_kMGT"[fl>>24]; 
+                    idx++;
+                    // If printing kibits and not in jedec, add the 'i'.
+                    if (fl&STBSP__METRIC_1024&&!(fl&STBSP__METRIC_JEDEC))
+                    {
+                        tail[idx+1]='i';
+                        idx++;
+                    }
+                    tail[0]=idx;
+                } 
+            } 
+        };
+
+        flt_lead:
+        // get the length that we copied
+        l = (stbsp__uint32) ( s-(num+64) );
+        s=num+64; 
+        goto scopy;
+#endif
+
+      case 'B': // upper binary
+        h = hexu;
+        goto binary;
+
+      case 'b': // lower binary
+        h = hex;
+        binary:
+        lead[0]=0;
+        if (fl&STBSP__LEADING_0X) { lead[0]=2;lead[1]='0';lead[2]=h[0xb]; }
+        l=(8<<4)|(1<<8);
+        goto radixnum;
+
+      case 'o': // octal
+        h = hexu;
+        lead[0]=0;
+        if (fl&STBSP__LEADING_0X) { lead[0]=1;lead[1]='0'; }
+        l=(3<<4)|(3<<8);
+        goto radixnum;
+
+      case 'p': // pointer
+        fl |= (sizeof(void*)==8)?STBSP__INTMAX:0;
+        pr = sizeof(void*)*2;
+        fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros
+        // drop through to X
+      
+      case 'X': // upper binary
+        h = hexu;
+        goto dohexb;
+
+      case 'x': // lower binary
+        h = hex; dohexb:
+        l=(4<<4)|(4<<8);
+        lead[0]=0;
+        if (fl&STBSP__LEADING_0X) { lead[0]=2;lead[1]='0';lead[2]=h[16]; }
+       radixnum: 
+        // get the number
+        if ( fl&STBSP__INTMAX )
+          n64 = va_arg(va,stbsp__uint64);
+        else
+          n64 = va_arg(va,stbsp__uint32);
+
+        s = num + STBSP__NUMSZ; dp = 0;
+        // clear tail, and clear leading if value is zero
+        tail[0]=0; if (n64==0) { lead[0]=0; if (pr==0) { l=0; cs = ( ((l>>4)&15)) << 24; goto scopy; } }
+        // convert to string
+        for(;;) { *--s = h[n64&((1<<(l>>8))-1)]; n64>>=(l>>8); if ( ! ( (n64) || ((stbsp__int32) ( (num+STBSP__NUMSZ) - s ) < pr ) ) ) break; if ( fl&STBSP__TRIPLET_COMMA) { ++l; if ((l&15)==((l>>4)&15)) { l&=~15; *--s=stbsp__comma; } } };
+        // get the tens and the comma pos
+        cs = (stbsp__uint32) ( (num+STBSP__NUMSZ) - s ) + ( ( ((l>>4)&15)) << 24 );
+        // get the length that we copied
+        l = (stbsp__uint32) ( (num+STBSP__NUMSZ) - s );
+        // copy it
+        goto scopy;
+
+      case 'u': // unsigned
+      case 'i':
+      case 'd': // integer
+        // get the integer and abs it
+        if ( fl&STBSP__INTMAX )
+        {
+          stbsp__int64 i64 = va_arg(va,stbsp__int64); n64 = (stbsp__uint64)i64; if ((f[0]!='u') && (i64<0)) { n64=(stbsp__uint64)-i64; fl|=STBSP__NEGATIVE; }
+        }
+        else
+        {
+          stbsp__int32 i = va_arg(va,stbsp__int32); n64 = (stbsp__uint32)i; if ((f[0]!='u') && (i<0)) { n64=(stbsp__uint32)-i; fl|=STBSP__NEGATIVE; }
+        }
+
+        #ifndef STB_SPRINTF_NOFLOAT
+        if (fl&STBSP__METRIC_SUFFIX) { if (n64<1024) pr=0; else if (pr==-1) pr=1; fv=(double)(stbsp__int64)n64; goto doafloat; } 
+        #endif
+
+        // convert to string
+        s = num+STBSP__NUMSZ; l=0; 
+        
+        for(;;)
+        {
+          // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
+          char * o=s-8;
+          if (n64>=100000000) { n = (stbsp__uint32)( n64 % 100000000);  n64 /= 100000000; } else {n = (stbsp__uint32)n64; n64 = 0; }
+          if((fl&STBSP__TRIPLET_COMMA)==0) { while(n) { s-=2; *(stbsp__uint16*)s=*(stbsp__uint16*)&stbsp__digitpair[(n%100)*2]; n/=100; } }
+          while (n) { if ( ( fl&STBSP__TRIPLET_COMMA) && (l++==3) ) { l=0; *--s=stbsp__comma; --o; } else { *--s=(char)(n%10)+'0'; n/=10; } }
+          if (n64==0) { if ((s[0]=='0') && (s!=(num+STBSP__NUMSZ))) ++s; break; }
+          while (s!=o) if ( ( fl&STBSP__TRIPLET_COMMA) && (l++==3) ) { l=0; *--s=stbsp__comma; --o; } else { *--s='0'; }
+        }
+
+        tail[0]=0;
+        stbsp__lead_sign(fl, lead);
+
+        // get the length that we copied
+        l = (stbsp__uint32) ( (num+STBSP__NUMSZ) - s ); if ( l == 0 ) { *--s='0'; l = 1; }
+        cs = l + (3<<24);
+        if (pr<0) pr = 0;
+
+       scopy: 
+        // get fw=leading/trailing space, pr=leading zeros
+        if (pr<(stbsp__int32)l) pr = l;
+        n = pr + lead[0] + tail[0] + tz;
+        if (fw<(stbsp__int32)n) fw = n;
+        fw -= n;
+        pr -= l;
+
+        // handle right justify and leading zeros
+        if ( (fl&STBSP__LEFTJUST)==0 )
+        {
+          if (fl&STBSP__LEADINGZERO) // if leading zeros, everything is in pr
+          { 
+            pr = (fw>pr)?fw:pr;
+            fw = 0;
+          }
+          else
+          {
+            fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas
+          }
+        }
+
+        // copy the spaces and/or zeros
+        if (fw+pr)
+        {
+          stbsp__int32 i; stbsp__uint32 c; 
+
+          // copy leading spaces (or when doing %8.4d stuff)
+          if ( (fl&STBSP__LEFTJUST)==0 ) while(fw>0) { stbsp__cb_buf_clamp(i,fw); fw -= i; while(i) { if ((((stbsp__uintptr)bf)&3)==0) break; *bf++=' '; --i; } while(i>=4) { *(stbsp__uint32*)bf=0x20202020; bf+=4; i-=4; } while (i) {*bf++=' '; --i;} stbsp__chk_cb_buf(1); }
+        
+          // copy leader
+          sn=lead+1; while(lead[0]) { stbsp__cb_buf_clamp(i,lead[0]); lead[0] -= (char)i; while (i) {*bf++=*sn++; --i;} stbsp__chk_cb_buf(1); }
+          
+          // copy leading zeros
+          c = cs >> 24; cs &= 0xffffff;
+          cs = (fl&STBSP__TRIPLET_COMMA)?((stbsp__uint32)(c-((pr+cs)%(c+1)))):0;
+          while(pr>0) { stbsp__cb_buf_clamp(i,pr); pr -= i; if((fl&STBSP__TRIPLET_COMMA)==0) { while(i) { if ((((stbsp__uintptr)bf)&3)==0) break; *bf++='0'; --i; } while(i>=4) { *(stbsp__uint32*)bf=0x30303030; bf+=4; i-=4; } } while (i) { if((fl&STBSP__TRIPLET_COMMA) && (cs++==c)) { cs = 0; *bf++=stbsp__comma; } else *bf++='0'; --i; } stbsp__chk_cb_buf(1); }
+        }
+
+        // copy leader if there is still one
+        sn=lead+1; while(lead[0]) { stbsp__int32 i; stbsp__cb_buf_clamp(i,lead[0]); lead[0] -= (char)i; while (i) {*bf++=*sn++; --i;} stbsp__chk_cb_buf(1); }
+
+        // copy the string
+        n = l; while (n) { stbsp__int32 i; stbsp__cb_buf_clamp(i,n); n-=i; STBSP__UNALIGNED( while(i>=4) { *(stbsp__uint32*)bf=*(stbsp__uint32*)s; bf+=4; s+=4; i-=4; } ) while (i) {*bf++=*s++; --i;} stbsp__chk_cb_buf(1); }
+
+        // copy trailing zeros
+        while(tz) { stbsp__int32 i; stbsp__cb_buf_clamp(i,tz); tz -= i; while(i) { if ((((stbsp__uintptr)bf)&3)==0) break; *bf++='0'; --i; } while(i>=4) { *(stbsp__uint32*)bf=0x30303030; bf+=4; i-=4; } while (i) {*bf++='0'; --i;} stbsp__chk_cb_buf(1); }
+
+        // copy tail if there is one
+        sn=tail+1; while(tail[0]) { stbsp__int32 i; stbsp__cb_buf_clamp(i,tail[0]); tail[0] -= (char)i; while (i) {*bf++=*sn++; --i;} stbsp__chk_cb_buf(1); }
+
+        // handle the left justify
+        if (fl&STBSP__LEFTJUST) if (fw>0) { while (fw) { stbsp__int32 i; stbsp__cb_buf_clamp(i,fw); fw-=i; while(i) { if ((((stbsp__uintptr)bf)&3)==0) break; *bf++=' '; --i; } while(i>=4) { *(stbsp__uint32*)bf=0x20202020; bf+=4; i-=4; } while (i--) *bf++=' '; stbsp__chk_cb_buf(1); } }
+        break;
+
+      default: // unknown, just copy code
+        s = num + STBSP__NUMSZ -1; *s = f[0];
+        l = 1;
+        fw=pr=fl=0;
+        lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
+        goto scopy;
+    }
+    ++f;
+  }
+ endfmt:
+
+  if (!callback) 
+    *bf = 0;
+  else
+    stbsp__flush_cb();
+ 
+ done:
+  return tlen + (int)(bf-buf);
+}
+
+// cleanup
+#undef STBSP__LEFTJUST
+#undef STBSP__LEADINGPLUS
+#undef STBSP__LEADINGSPACE
+#undef STBSP__LEADING_0X
+#undef STBSP__LEADINGZERO
+#undef STBSP__INTMAX
+#undef STBSP__TRIPLET_COMMA
+#undef STBSP__NEGATIVE
+#undef STBSP__METRIC_SUFFIX
+#undef STBSP__NUMSZ
+#undef stbsp__chk_cb_bufL
+#undef stbsp__chk_cb_buf
+#undef stbsp__flush_cb
+#undef stbsp__cb_buf_clamp
+
+// ============================================================================
+//   wrapper functions
+
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( sprintf )( char * buf, char const * fmt, ... )
+{
+  int result;
+  va_list va;
+  va_start( va, fmt );
+  result = STB_SPRINTF_DECORATE( vsprintfcb )( 0, 0, buf, fmt, va );
+  va_end(va);
+  return result;
+}
+
+typedef struct stbsp__context
+{
+  char * buf;
+  int count;
+  char tmp[ STB_SPRINTF_MIN ];
+} stbsp__context;
+
+static char * stbsp__clamp_callback( char * buf, void * user, int len )
+{
+  stbsp__context * c = (stbsp__context*)user;
+
+  if ( len > c->count ) len = c->count;
+
+  if (len)
+  {
+    if ( buf != c->buf )
+    {
+      char * s, * d, * se;
+      d = c->buf; s = buf; se = buf+len;
+      do{ *d++ = *s++; } while (s<se);
+    }
+    c->buf += len;
+    c->count -= len;
+  }
+  
+  if ( c->count <= 0 ) return 0;
+  return ( c->count >= STB_SPRINTF_MIN ) ? c->buf : c->tmp; // go direct into buffer if you can
+}
+
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )
+{
+  stbsp__context c;
+  int l;
+
+  if ( count == 0 )
+    return 0;
+
+  c.buf = buf;
+  c.count = count;
+
+  STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va );
+  
+  // zero-terminate
+  l = (int)( c.buf - buf );
+  if ( l >= count ) // should never be greater, only equal (or less) than count
+    l = count - 1;
+  buf[l] = 0;
+
+  return l;
+}
+
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( snprintf )( char * buf, int count, char const * fmt, ... )
+{
+  int result;
+  va_list va;
+  va_start( va, fmt );
+
+  result = STB_SPRINTF_DECORATE( vsnprintf )( buf, count, fmt, va );
+  va_end(va);
+
+  return result;
+}
+
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsprintf )( char * buf, char const * fmt, va_list va )
+{
+  return STB_SPRINTF_DECORATE( vsprintfcb )( 0, 0, buf, fmt, va );
+}
+
+// =======================================================================
+//   low level float utility functions
+
+#ifndef STB_SPRINTF_NOFLOAT
+
+// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
+#define STBSP__COPYFP(dest,src) { int cn; for(cn=0;cn<8;cn++) ((char*)&dest)[cn]=((char*)&src)[cn]; }
+ 
+// get float info
+static stbsp__int32 stbsp__real_to_parts( stbsp__int64 * bits, stbsp__int32 * expo, double value )
+{
+  double d;
+  stbsp__int64 b = 0;
+
+  // load value and round at the frac_digits
+  d = value;
+
+  STBSP__COPYFP( b, d );
+
+  *bits = b & ((((stbsp__uint64)1)<<52)-1);
+  *expo = (stbsp__int32) (((b >> 52) & 2047)-1023);
+    
+  return (stbsp__int32)(b >> 63);
+}
+
+static double const stbsp__bot[23]={1e+000,1e+001,1e+002,1e+003,1e+004,1e+005,1e+006,1e+007,1e+008,1e+009,1e+010,1e+011,1e+012,1e+013,1e+014,1e+015,1e+016,1e+017,1e+018,1e+019,1e+020,1e+021,1e+022};
+static double const stbsp__negbot[22]={1e-001,1e-002,1e-003,1e-004,1e-005,1e-006,1e-007,1e-008,1e-009,1e-010,1e-011,1e-012,1e-013,1e-014,1e-015,1e-016,1e-017,1e-018,1e-019,1e-020,1e-021,1e-022};
+static double const stbsp__negboterr[22]={-5.551115123125783e-018,-2.0816681711721684e-019,-2.0816681711721686e-020,-4.7921736023859299e-021,-8.1803053914031305e-022,4.5251888174113741e-023,4.5251888174113739e-024,-2.0922560830128471e-025,-6.2281591457779853e-026,-3.6432197315497743e-027,6.0503030718060191e-028,2.0113352370744385e-029,-3.0373745563400371e-030,1.1806906454401013e-032,-7.7705399876661076e-032,2.0902213275965398e-033,-7.1542424054621921e-034,-7.1542424054621926e-035,2.4754073164739869e-036,5.4846728545790429e-037,9.2462547772103625e-038,-4.8596774326570872e-039};
+static double const stbsp__top[13]={1e+023,1e+046,1e+069,1e+092,1e+115,1e+138,1e+161,1e+184,1e+207,1e+230,1e+253,1e+276,1e+299};
+static double const stbsp__negtop[13]={1e-023,1e-046,1e-069,1e-092,1e-115,1e-138,1e-161,1e-184,1e-207,1e-230,1e-253,1e-276,1e-299};
+static double const stbsp__toperr[13]={8388608,6.8601809640529717e+028,-7.253143638152921e+052,-4.3377296974619174e+075,-1.5559416129466825e+098,-3.2841562489204913e+121,-3.7745893248228135e+144,-1.7356668416969134e+167,-3.8893577551088374e+190,-9.9566444326005119e+213,6.3641293062232429e+236,-5.2069140800249813e+259,-5.2504760255204387e+282};
+static double const stbsp__negtoperr[13]={3.9565301985100693e-040,-2.299904345391321e-063,3.6506201437945798e-086,1.1875228833981544e-109,-5.0644902316928607e-132,-6.7156837247865426e-155,-2.812077463003139e-178,-5.7778912386589953e-201,7.4997100559334532e-224,-4.6439668915134491e-247,-6.3691100762962136e-270,-9.436808465446358e-293,8.0970921678014997e-317};
+
+#if defined(_MSC_VER) && (_MSC_VER<=1200)                                                                                                                                                                                       
+static stbsp__uint64 const stbsp__powten[20]={1,10,100,1000, 10000,100000,1000000,10000000, 100000000,1000000000,10000000000,100000000000,  1000000000000,10000000000000,100000000000000,1000000000000000,  10000000000000000,100000000000000000,1000000000000000000,10000000000000000000U };
+#define stbsp__tento19th ((stbsp__uint64)1000000000000000000)
+#else
+static stbsp__uint64 const stbsp__powten[20]={1,10,100,1000, 10000,100000,1000000,10000000, 100000000,1000000000,10000000000ULL,100000000000ULL,  1000000000000ULL,10000000000000ULL,100000000000000ULL,1000000000000000ULL,  10000000000000000ULL,100000000000000000ULL,1000000000000000000ULL,10000000000000000000ULL };
+#define stbsp__tento19th (1000000000000000000ULL)
+#endif
+
+#define stbsp__ddmulthi(oh,ol,xh,yh) \
+{ \
+  double ahi=0,alo,bhi=0,blo; \
+  stbsp__int64 bt; \
+  oh = xh * yh; \
+  STBSP__COPYFP(bt,xh); bt&=((~(stbsp__uint64)0)<<27); STBSP__COPYFP(ahi,bt); alo = xh-ahi; \
+  STBSP__COPYFP(bt,yh); bt&=((~(stbsp__uint64)0)<<27); STBSP__COPYFP(bhi,bt); blo = yh-bhi; \
+  ol = ((ahi*bhi-oh)+ahi*blo+alo*bhi)+alo*blo; \
+}
+
+#define stbsp__ddtoS64(ob,xh,xl) \
+{ \
+  double ahi=0,alo,vh,t;\
+  ob = (stbsp__int64)ph;\
+  vh=(double)ob;\
+  ahi = ( xh - vh );\
+  t = ( ahi - xh );\
+  alo = (xh-(ahi-t))-(vh+t);\
+  ob += (stbsp__int64)(ahi+alo+xl);\
+}
+
+
+#define stbsp__ddrenorm(oh,ol) { double s; s=oh+ol; ol=ol-(s-oh); oh=s; }
+
+#define stbsp__ddmultlo(oh,ol,xh,xl,yh,yl) \
+  ol = ol + ( xh*yl + xl*yh ); \
+
+#define stbsp__ddmultlos(oh,ol,xh,yl) \
+  ol = ol + ( xh*yl ); \
+
+static void stbsp__raise_to_power10( double *ohi, double *olo, double d, stbsp__int32 power )  // power can be -323 to +350
+{
+  double ph, pl;
+  if ((power>=0) && (power<=22))
+  {
+    stbsp__ddmulthi(ph,pl,d,stbsp__bot[power]);
+  }
+  else
+  {
+    stbsp__int32 e,et,eb;
+    double p2h,p2l;
+
+    e=power; if (power<0) e=-e; 
+    et = (e*0x2c9)>>14;/* %23 */ if (et>13) et=13; eb = e-(et*23);
+
+    ph = d; pl = 0.0;
+    if (power<0)
+    {
+      if (eb) { --eb; stbsp__ddmulthi(ph,pl,d,stbsp__negbot[eb]); stbsp__ddmultlos(ph,pl,d,stbsp__negboterr[eb]); }
+      if (et)
+      { 
+        stbsp__ddrenorm(ph,pl);
+        --et; stbsp__ddmulthi(p2h,p2l,ph,stbsp__negtop[et]); stbsp__ddmultlo(p2h,p2l,ph,pl,stbsp__negtop[et],stbsp__negtoperr[et]); ph=p2h;pl=p2l;
+      }
+    }
+    else
+    {
+      if (eb) 
+      { 
+        e = eb; if (eb>22) eb=22; e -= eb;
+        stbsp__ddmulthi(ph,pl,d,stbsp__bot[eb]); 
+        if ( e ) { stbsp__ddrenorm(ph,pl); stbsp__ddmulthi(p2h,p2l,ph,stbsp__bot[e]); stbsp__ddmultlos(p2h,p2l,stbsp__bot[e],pl); ph=p2h;pl=p2l; }
+      }
+      if (et)
+      {
+        stbsp__ddrenorm(ph,pl);
+        --et; stbsp__ddmulthi(p2h,p2l,ph,stbsp__top[et]); stbsp__ddmultlo(p2h,p2l,ph,pl,stbsp__top[et],stbsp__toperr[et]); ph=p2h;pl=p2l;
+      }
+    }
+  }
+  stbsp__ddrenorm(ph,pl);
+  *ohi = ph; *olo = pl;
+}
+
+// given a float value, returns the significant bits in bits, and the position of the
+//   decimal point in decimal_pos.  +/-INF and NAN are specified by special values
+//   returned in the decimal_pos parameter.
+// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
+static stbsp__int32 stbsp__real_to_str( char const * * start, stbsp__uint32 * len, char *out, stbsp__int32 * decimal_pos, double value, stbsp__uint32 frac_digits )
+{
+  double d;
+  stbsp__int64 bits = 0;
+  stbsp__int32 expo, e, ng, tens;
+
+  d = value;
+  STBSP__COPYFP(bits,d);
+  expo = (stbsp__int32) ((bits >> 52) & 2047);
+  ng = (stbsp__int32)(bits >> 63);
+  if (ng) d=-d;
+
+  if ( expo == 2047 ) // is nan or inf?
+  {
+    *start = (bits&((((stbsp__uint64)1)<<52)-1)) ? "NaN" : "Inf";
+    *decimal_pos =  STBSP__SPECIAL;
+    *len = 3;
+    return ng;
+  } 
+
+  if ( expo == 0 ) // is zero or denormal
+  {
+    if ((bits<<1)==0) // do zero
+    {
+      *decimal_pos = 1; 
+      *start = out;
+      out[0] = '0'; *len = 1;
+      return ng;
+    }
+    // find the right expo for denormals
+    {
+      stbsp__int64 v = ((stbsp__uint64)1)<<51;
+      while ((bits&v)==0) { --expo; v >>= 1; }
+    }
+  }
+
+  // find the decimal exponent as well as the decimal bits of the value
+  {
+    double ph,pl;
+
+    // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
+    tens=expo-1023; tens = (tens<0)?((tens*617)/2048):(((tens*1233)/4096)+1);
+
+    // move the significant bits into position and stick them into an int 
+    stbsp__raise_to_power10( &ph, &pl, d, 18-tens );
+
+    // get full as much precision from double-double as possible
+    stbsp__ddtoS64( bits, ph,pl );
+
+    // check if we undershot
+    if ( ((stbsp__uint64)bits) >= stbsp__tento19th ) ++tens; 
+  }
+
+  // now do the rounding in integer land
+  frac_digits = ( frac_digits & 0x80000000 ) ? ( (frac_digits&0x7ffffff) + 1 ) : ( tens + frac_digits );
+  if ( ( frac_digits < 24 ) )
+  {
+    stbsp__uint32 dg = 1; if ((stbsp__uint64)bits >= stbsp__powten[9] ) dg=10; while( (stbsp__uint64)bits >= stbsp__powten[dg] ) { ++dg; if (dg==20) goto noround; }
+    if ( frac_digits < dg )
+    {
+      stbsp__uint64 r;
+      // add 0.5 at the right position and round
+      e = dg - frac_digits;
+      if ( (stbsp__uint32)e >= 24 ) goto noround;
+      r = stbsp__powten[e];
+      bits = bits + (r/2);
+      if ( (stbsp__uint64)bits >= stbsp__powten[dg] ) ++tens;
+      bits /= r;
+    } 
+    noround:;
+  }
+
+  // kill long trailing runs of zeros
+  if ( bits )
+  {
+    stbsp__uint32 n;
+    for(;;) { if ( bits<=0xffffffff ) break; if (bits%1000) goto donez; bits/=1000; }
+    n = (stbsp__uint32)bits;
+    while ((n%1000)==0) n/=1000;
+    bits=n;
+    donez:;
+  }
+
+  // convert to string
+  out += 64;
+  e = 0; 
+  for(;;)
+  {
+    stbsp__uint32 n;
+    char * o = out-8;
+    // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
+    if (bits>=100000000) { n = (stbsp__uint32)( bits % 100000000);  bits /= 100000000; } else {n = (stbsp__uint32)bits; bits = 0; }
+    while(n) { out-=2; *(stbsp__uint16*)out=*(stbsp__uint16*)&stbsp__digitpair[(n%100)*2]; n/=100; e+=2; }
+    if (bits==0) { if ((e) && (out[0]=='0')) { ++out; --e; } break; }
+    while( out!=o ) { *--out ='0'; ++e; }
+  }
+  
+  *decimal_pos = tens;
+  *start = out;
+  *len = e;
+  return ng;
+}
+
+#undef stbsp__ddmulthi
+#undef stbsp__ddrenorm
+#undef stbsp__ddmultlo
+#undef stbsp__ddmultlos
+#undef STBSP__SPECIAL 
+#undef STBSP__COPYFP
+ 
+#endif // STB_SPRINTF_NOFLOAT
+
+// clean up
+#undef stbsp__uint16
+#undef stbsp__uint32 
+#undef stbsp__int32 
+#undef stbsp__uint64
+#undef stbsp__int64
+#undef STBSP__UNALIGNED
+
+#endif // STB_SPRINTF_IMPLEMENTATION
+
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
+software, either in source code form or as a compiled binary, for any purpose, 
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this 
+software dedicate any and all copyright interest in the software to the public 
+domain. We make this dedication for the benefit of the public at large and to 
+the detriment of our heirs and successors. We intend this dedication to be an 
+overt act of relinquishment in perpetuity of all present and future rights to 
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/

+ 165 - 62
stb_textedit.h

@@ -1,4 +1,4 @@
-// stb_textedit.h - v1.6  - public domain - Sean Barrett
+// stb_textedit.h - v1.11  - public domain - Sean Barrett
 // Development of this library was sponsored by RAD Game Tools
 //
 // This C header file implements the guts of a multi-line text-editing
@@ -17,9 +17,7 @@
 //
 // LICENSE
 //
-// This software has been placed in the public domain by its author.
-// Where that dedication is not recognized, you are granted a perpetual,
-// irrevocable license to copy and modify this file as you see fit.
+// See end of file for license information.
 //
 //
 // DEPENDENCIES
@@ -31,6 +29,11 @@
 //
 // VERSION HISTORY
 //
+//   1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
+//   1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual
+//   1.9  (2016-08-27) customizable move-by-word
+//   1.8  (2016-04-02) better keyboard handling when mouse button is down
+//   1.7  (2015-09-13) change y range handling in case baseline is non-0
 //   1.6  (2015-04-15) allow STB_TEXTEDIT_memmove
 //   1.5  (2014-09-10) add support for secondary keys for OS X
 //   1.4  (2014-08-17) fix signed/unsigned warnings
@@ -45,9 +48,14 @@
 // ADDITIONAL CONTRIBUTORS
 //
 //   Ulf Winklemann: move-by-word in 1.1
-//   Scott Graham: mouse selection bugfix in 1.3
 //   Fabian Giesen: secondary key inputs in 1.5
-//   Martins Mozeiko: STB_TEXTEDIT_memmove
+//   Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6
+//
+//   Bugfixes:
+//      Scott Graham
+//      Daniel Keller
+//      Omar Cornut
+//      Dan Thompson
 //
 // USAGE
 //
@@ -142,15 +150,17 @@
 //    STB_TEXTEDIT_K_REDO        keyboard input to perform redo
 //
 // Optional:
-//    STB_TEXTEDIT_K_INSERT      keyboard input to toggle insert mode
-//    STB_TEXTEDIT_IS_SPACE(ch)  true if character is whitespace (e.g. 'isspace'),
-//                                 required for WORDLEFT/WORDRIGHT
-//    STB_TEXTEDIT_K_WORDLEFT    keyboard input to move cursor left one word // e.g. ctrl-LEFT
-//    STB_TEXTEDIT_K_WORDRIGHT   keyboard input to move cursor right one word // e.g. ctrl-RIGHT
-//    STB_TEXTEDIT_K_LINESTART2  secondary keyboard input to move cursor to start of line
-//    STB_TEXTEDIT_K_LINEEND2    secondary keyboard input to move cursor to end of line
-//    STB_TEXTEDIT_K_TEXTSTART2  secondary keyboard input to move cursor to start of text
-//    STB_TEXTEDIT_K_TEXTEND2    secondary keyboard input to move cursor to end of text
+//    STB_TEXTEDIT_K_INSERT              keyboard input to toggle insert mode
+//    STB_TEXTEDIT_IS_SPACE(ch)          true if character is whitespace (e.g. 'isspace'),
+//                                          required for default WORDLEFT/WORDRIGHT handlers
+//    STB_TEXTEDIT_MOVEWORDLEFT(obj,i)   custom handler for WORDLEFT, returns index to move cursor to
+//    STB_TEXTEDIT_MOVEWORDRIGHT(obj,i)  custom handler for WORDRIGHT, returns index to move cursor to
+//    STB_TEXTEDIT_K_WORDLEFT            keyboard input to move cursor left one word // e.g. ctrl-LEFT
+//    STB_TEXTEDIT_K_WORDRIGHT           keyboard input to move cursor right one word // e.g. ctrl-RIGHT
+//    STB_TEXTEDIT_K_LINESTART2          secondary keyboard input to move cursor to start of line
+//    STB_TEXTEDIT_K_LINEEND2            secondary keyboard input to move cursor to end of line
+//    STB_TEXTEDIT_K_TEXTSTART2          secondary keyboard input to move cursor to start of text
+//    STB_TEXTEDIT_K_TEXTEND2            secondary keyboard input to move cursor to end of text
 //
 // Todo:
 //    STB_TEXTEDIT_K_PGUP        keyboard input to move cursor up a page
@@ -380,9 +390,6 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
    float base_y = 0, prev_x;
    int i=0, k;
 
-   if (y < 0)
-      return 0;
-
    r.x0 = r.x1 = 0;
    r.ymin = r.ymax = 0;
    r.num_chars = 0;
@@ -393,6 +400,9 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
       if (r.num_chars <= 0)
          return n;
 
+      if (i==0 && y < base_y + r.ymin)
+         return 0;
+
       if (y < base_y + r.ymax)
          break;
 
@@ -411,10 +421,9 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
    // check if it's before the end of the line
    if (x < r.x1) {
       // search characters in row for one that straddles 'x'
-      k = i;
       prev_x = r.x0;
-      for (i=0; i < r.num_chars; ++i) {
-         float w = STB_TEXTEDIT_GETWIDTH(str, k, i);
+      for (k=0; k < r.num_chars; ++k) {
+         float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
          if (x < prev_x+w) {
             if (x < prev_x+w/2)
                return k+i;
@@ -436,6 +445,15 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
 // API click: on mouse down, move the cursor to the clicked location, and reset the selection
 static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
 {
+   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
+   // goes off the top or bottom of the text
+   if( state->single_line )
+   {
+      StbTexteditRow r;
+      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
+      y = r.ymin;
+   }
+
    state->cursor = stb_text_locate_coord(str, x, y);
    state->select_start = state->cursor;
    state->select_end = state->cursor;
@@ -445,7 +463,21 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
 // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
 static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
 {
-   int p = stb_text_locate_coord(str, x, y);
+   int p = 0;
+
+   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
+   // goes off the top or bottom of the text
+   if( state->single_line )
+   {
+      StbTexteditRow r;
+      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
+      y = r.ymin;
+   }
+
+   if (state->select_start == state->select_end)
+      state->select_start = state->cursor;
+
+   p = stb_text_locate_coord(str, x, y);
    state->cursor = state->select_end = p;
 }
 
@@ -602,15 +634,16 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
 }
 
 #ifdef STB_TEXTEDIT_IS_SPACE
-static int is_word_boundary( STB_TEXTEDIT_STRING *_str, int _idx )
+static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
 {
-   return _idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str,_idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str, _idx) ) ) : 1;
+   return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
 }
 
-static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state )
+#ifndef STB_TEXTEDIT_MOVEWORDLEFT
+static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
 {
-   int c = _state->cursor - 1;
-   while( c >= 0 && !is_word_boundary( _str, c ) )
+   --c; // always move at least one character
+   while( c >= 0 && !is_word_boundary( str, c ) )
       --c;
 
    if( c < 0 )
@@ -618,12 +651,15 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, STB_Te
 
    return c;
 }
+#define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous
+#endif
 
-static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state )
+#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
+static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
 {
-   const int len = STB_TEXTEDIT_STRINGLEN(_str);
-   int c = _state->cursor+1;
-   while( c < len && !is_word_boundary( _str, c ) )
+   const int len = STB_TEXTEDIT_STRINGLEN(str);
+   ++c; // always move at least one character
+   while( c < len && !is_word_boundary( str, c ) )
       ++c;
 
    if( c > len )
@@ -631,6 +667,9 @@ static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *_str, STB_Texted
 
    return c;
 }
+#define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next
+#endif
+
 #endif
 
 // update selection and cursor to match each other
@@ -654,9 +693,8 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
 }
 
 // API paste: replace existing selection with passed-in text
-static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
+static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
 {
-   STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext;
    // if there's a selection, the paste should delete it
    stb_textedit_clamp(str, state);
    stb_textedit_delete_selection(str,state);
@@ -752,21 +790,12 @@ retry:
          state->has_preferred_x = 0;
          break;
 
-#ifdef STB_TEXTEDIT_IS_SPACE
+#ifdef STB_TEXTEDIT_MOVEWORDLEFT
       case STB_TEXTEDIT_K_WORDLEFT:
          if (STB_TEXT_HAS_SELECTION(state))
             stb_textedit_move_to_first(state);
          else {
-            state->cursor = stb_textedit_move_to_word_previous(str, state);
-            stb_textedit_clamp( str, state );
-         }
-         break;
-
-      case STB_TEXTEDIT_K_WORDRIGHT:
-         if (STB_TEXT_HAS_SELECTION(state)) 
-            stb_textedit_move_to_last(str, state);
-         else {
-            state->cursor = stb_textedit_move_to_word_next(str, state);
+            state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);
             stb_textedit_clamp( str, state );
          }
          break;
@@ -775,17 +804,28 @@ retry:
          if( !STB_TEXT_HAS_SELECTION( state ) )
             stb_textedit_prep_selection_at_cursor(state);
 
-         state->cursor = stb_textedit_move_to_word_previous(str, state);
+         state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);
          state->select_end = state->cursor;
 
          stb_textedit_clamp( str, state );
          break;
+#endif
+
+#ifdef STB_TEXTEDIT_MOVEWORDRIGHT
+      case STB_TEXTEDIT_K_WORDRIGHT:
+         if (STB_TEXT_HAS_SELECTION(state)) 
+            stb_textedit_move_to_last(str, state);
+         else {
+            state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);
+            stb_textedit_clamp( str, state );
+         }
+         break;
 
       case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT:
          if( !STB_TEXT_HAS_SELECTION( state ) )
             stb_textedit_prep_selection_at_cursor(state);
 
-         state->cursor = stb_textedit_move_to_word_next(str, state);
+         state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);
          state->select_end = state->cursor;
 
          stb_textedit_clamp( str, state );
@@ -968,25 +1008,27 @@ retry:
 #ifdef STB_TEXTEDIT_K_LINESTART2
       case STB_TEXTEDIT_K_LINESTART2:
 #endif
-      case STB_TEXTEDIT_K_LINESTART: {
-         StbFindState find;
+      case STB_TEXTEDIT_K_LINESTART:
          stb_textedit_clamp(str, state);
          stb_textedit_move_to_first(state);
-         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
-         state->cursor = find.first_char;
+         if (state->single_line)
+            state->cursor = 0;
+         else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
+            --state->cursor;
          state->has_preferred_x = 0;
          break;
-      }
 
 #ifdef STB_TEXTEDIT_K_LINEEND2
       case STB_TEXTEDIT_K_LINEEND2:
 #endif
       case STB_TEXTEDIT_K_LINEEND: {
-         StbFindState find;
+         int n = STB_TEXTEDIT_STRINGLEN(str);
          stb_textedit_clamp(str, state);
          stb_textedit_move_to_first(state);
-         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
-         state->cursor = find.first_char + find.length;
+         if (state->single_line)
+             state->cursor = n;
+         else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
+             ++state->cursor;
          state->has_preferred_x = 0;
          break;
       }
@@ -994,25 +1036,29 @@ retry:
 #ifdef STB_TEXTEDIT_K_LINESTART2
       case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT:
 #endif
-      case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: {
-         StbFindState find;
+      case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
          stb_textedit_clamp(str, state);
          stb_textedit_prep_selection_at_cursor(state);
-         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
-         state->cursor = state->select_end = find.first_char;
+         if (state->single_line)
+            state->cursor = 0;
+         else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
+            --state->cursor;
+         state->select_end = state->cursor;
          state->has_preferred_x = 0;
          break;
-      }
 
 #ifdef STB_TEXTEDIT_K_LINEEND2
       case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
 #endif
       case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
-         StbFindState find;
+         int n = STB_TEXTEDIT_STRINGLEN(str);
          stb_textedit_clamp(str, state);
          stb_textedit_prep_selection_at_cursor(state);
-         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
-         state->cursor = state->select_end = find.first_char + find.length;
+         if (state->single_line)
+             state->cursor = n;
+         else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
+            ++state->cursor;
+         state->select_end = state->cursor;
          state->has_preferred_x = 0;
          break;
       }
@@ -1287,4 +1333,61 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
 {
    stb_textedit_clear_state(state, is_single_line);
 }
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+
+static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
+{
+   return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);
+}
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
 #endif//STB_TEXTEDIT_IMPLEMENTATION
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
+software, either in source code form or as a compiled binary, for any purpose, 
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this 
+software dedicate any and all copyright interest in the software to the public 
+domain. We make this dedication for the benefit of the public at large and to 
+the detriment of our heirs and successors. We intend this dedication to be an 
+overt act of relinquishment in perpetuity of all present and future rights to 
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/

+ 59 - 7
stb_tilemap_editor.h

@@ -1,4 +1,4 @@
-// stb_tilemap_editor.h - v0.35 - Sean Barrett - http://nothings.org/stb
+// stb_tilemap_editor.h - v0.38 - Sean Barrett - http://nothings.org/stb
 // placed in the public domain - not copyrighted - first released 2014-09
 //
 // Embeddable tilemap editor for C/C++
@@ -259,7 +259,7 @@
 //      #define STBTE_MAX_CATEGORIES     100
 //      #define STBTE_UNDO_BUFFER_BYTES  (1 << 24) // 16 MB
 //      #define STBTE_MAX_COPY           90000  // e.g. 300x300
-//      #define STBTE_MAX_PROPERTIESERTIES     10     // max properties per tile
+//      #define STBTE_MAX_PROPERTIES     10     // max properties per tile
 //
 // API
 //
@@ -275,6 +275,9 @@
 //   either approach allows cut&pasting between levels.)
 //
 // REVISION HISTORY
+//   0.38  fix warning
+//   0.37  fix warning
+//   0.36  minor compiler support
 //   0.35  layername button changes
 //          - layername buttons grow with the layer panel
 //          - fix stbte_create_map being declared as stbte_create
@@ -309,13 +312,12 @@
 //   Additional features:
 //      Josh Huelsman
 //   Bugfixes:
-//      [this could be you!]
+//      Ryan Whitworth
+//      Eugene Opalev
 //
 // LICENSE
 //
-//   This software has been placed in the public domain by its author.
-//   Where that dedication is not recognized, you are granted a perpetual,
-//   irrevocable license to copy and modify this file as you see fit.
+//   See end of file for license information.
 
 
 
@@ -326,6 +328,14 @@
 #ifndef STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
 #define STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
 
+#ifdef _WIN32
+  #ifndef _CRT_SECURE_NO_WARNINGS
+  #define _CRT_SECURE_NO_WARNINGS
+  #endif
+  #include <stdlib.h>
+  #include <stdio.h>
+#endif
+
 typedef struct stbte_tilemap stbte_tilemap;
 
 // these are the drawmodes used in STBTE_DRAW_TILE
@@ -3345,7 +3355,7 @@ static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h)
 
 #define STBTE__TEXTCOLOR(n)  stbte__color_table[n][STBTE__text][STBTE__idle]
 
-static int stbte__info_value(char *label, int x, int y, int val, int digits, int id)
+static int stbte__info_value(const char *label, int x, int y, int val, int digits, int id)
 {
    if (stbte__ui.event == STBTE__paint) {
       int off = 9-stbte__get_char_width(label[0]);
@@ -4118,3 +4128,45 @@ void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xs, float y
 }
 
 #endif // STB_TILEMAP_EDITOR_IMPLEMENTATION
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
+software, either in source code form or as a compiled binary, for any purpose, 
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this 
+software dedicate any and all copyright interest in the software to the public 
+domain. We make this dedication for the benefit of the public at large and to 
+the detriment of our heirs and successors. We intend this dedication to be an 
+overt act of relinquishment in perpetuity of all present and future rights to 
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/

文件差異過大導致無法顯示
+ 743 - 54
stb_truetype.h


文件差異過大導致無法顯示
+ 210 - 247
stb_vorbis.c


+ 78 - 13
stb_voxel_render.h

@@ -1,4 +1,4 @@
-// stb_voxel_render.h - v0.81 - Sean Barrett, 2015 - public domain
+// stb_voxel_render.h - v0.85 - Sean Barrett, 2015 - public domain
 //
 // This library helps render large-scale "voxel" worlds for games,
 // in this case, one with blocks that can have textures and that
@@ -13,7 +13,7 @@
 // It works by creating triangle meshes. The library includes
 //
 //    - converter from dense 3D arrays of block info to vertex mesh
-//    - shader for the vertex mesh
+//    - vertex & fragment shaders for the vertex mesh
 //    - assistance in setting up shader state
 //
 // For portability, none of the library code actually accesses
@@ -24,8 +24,9 @@
 // yourself. However, you could also try making a game with
 // a small enough world that it's fully loaded rather than
 // streaming. Currently the preferred vertex format is 20 bytes
-// per quad. There are plans to allow much more compact formats
-// with a slight reduction in shader features.
+// per quad. There are designs to allow much more compact formats
+// with a slight reduction in shader features, but no roadmap
+// for actually implementing them.
 //
 //
 // USAGE
@@ -108,7 +109,7 @@
 //        and avoids a potential slow path (gathering non-uniform
 //        data from uniforms) on some hardware.
 //
-//   In the future I hope to add additional modes that have significantly
+//   In the future I might add additional modes that have significantly
 //   smaller meshes but reduce features, down as small as 6 bytes per quad.
 //   See elsewhere in this file for a table of candidate modes. Switching
 //   to a mode will require changing some of your mesh creation code, but
@@ -186,11 +187,16 @@
 //   Features             Porting            Bugfixes & Warnings
 //  Sean Barrett                          github:r-leyh   Jesus Fernandez
 //                                        Miguel Lechon   github:Arbeiterunfallversicherungsgesetz
-//                                        Thomas Frase
+//                                        Thomas Frase    James Hofmann
+//                                        Stephen Olsen   github:guitarfreak
 //
 // VERSION HISTORY
 //
+//   0.85   (2017-03-03)  add block_selector (by guitarfreak)
+//   0.84   (2016-04-02)  fix GLSL syntax error on glModelView path
+//   0.83   (2015-09-13)  remove non-constant struct initializers to support more compilers
 //   0.82   (2015-08-01)  added input.packed_compact to store rot, vheight & texlerp efficiently
+//                        fix broken tex_overlay2
 //   0.81   (2015-05-28)  fix broken STBVOX_CONFIG_OPTIMIZED_VHEIGHT
 //   0.80   (2015-04-11)  fix broken STBVOX_CONFIG_ROTATION_IN_LIGHTING refactoring
 //                        change STBVOX_MAKE_LIGHTING to STBVOX_MAKE_LIGHTING_EXT so
@@ -209,6 +215,11 @@
 //   stb_voxel_render   20-byte quads   2015/01
 //   zmc engine         32-byte quads   2013/12
 //   zmc engine         96-byte quads   2011/10
+//
+//
+// LICENSE
+//
+//   See end of file for license information.
 
 #ifndef INCLUDE_STB_VOXEL_RENDER_H
 #define INCLUDE_STB_VOXEL_RENDER_H
@@ -253,7 +264,7 @@ extern "C" {
 //     modes 0,1,20,21, Z in the mesh can extend to 511 instead
 //     of 255. However, half-height blocks cannot be used.
 //
-// All of the following just #ifdef tested so need no values, and are optional.
+// All of the following are just #ifdef tested so need no values, and are optional.
 //
 //    STBVOX_CONFIG_BLOCKTYPE_SHORT
 //        use unsigned 16-bit values for 'blocktype' in the input instead of 8-bit values
@@ -293,7 +304,7 @@ extern "C" {
 //
 //    STBVOX_CONFIG_DISABLE_TEX2
 //        This disables all processing of texture 2 in the shader in case
-//        you don't use it. Eventually this will be replaced with a mode
+//        you don't use it. Eventually this could be replaced with a mode
 //        that omits the unused data entirely.
 //
 //    STBVOX_CONFIG_TEX1_EDGE_CLAMP
@@ -1581,7 +1592,7 @@ static const char *stbvox_vertex_program =
       "uniform vec3 normal_table[32];\n"
 
       #ifndef STBVOX_CONFIG_OPENGL_MODELVIEW
-         "uniform mat44 model_view;\n"
+         "uniform mat4x4 model_view;\n"
       #endif
 
       // fragment output data
@@ -2891,7 +2902,9 @@ static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, in
 
    if (mm->input.selector)
       mesh = mm->input.selector[v_off];
-
+   else if (mm->input.block_selector)
+      mesh = mm->input.block_selector[mm->input.blocktype[v_off]];
+  
    // check if we're going off the end
    if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) {
       mm->full = 1;
@@ -3100,7 +3113,9 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po
    mesh = mm->default_mesh;
    if (mm->input.selector)
       mesh = mm->input.selector[v_off];
-
+   else if (mm->input.block_selector)
+      mesh = mm->input.block_selector[bt];
+  
    if (geo <= STBVOX_GEOM_ceil_slope_north_is_bottom) {
       // this is the simple case, we can just use regular block gen with special vmesh calculated with vheight
       stbvox_mesh_vertex basevert;
@@ -3121,7 +3136,9 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po
       basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0);
       if (mm->input.selector) {
          mesh = mm->input.selector[v_off];
-      }
+      } else if (mm->input.block_selector)
+         mesh = mm->input.block_selector[bt];
+
 
       // check if we're going off the end
       if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) {
@@ -3341,6 +3358,9 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po
          mesh = mm->input.selector[v_off];
          simple_rot = mesh >> 4;
          mesh &= 15;
+      } 
+      if (mm->input.block_selector) {
+         mesh = mm->input.block_selector[bt];
       }
 
       // check if we're going off the end
@@ -3374,10 +3394,13 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po
 
 static void stbvox_make_mesh_for_column(stbvox_mesh_maker *mm, int x, int y, int z0)
 {
-   stbvox_pos pos = { x,y,0 };
+   stbvox_pos pos;
    int v_off = x * mm->x_stride_in_bytes + y * mm->y_stride_in_bytes;
    int ns_off = mm->y_stride_in_bytes;
    int ew_off = mm->x_stride_in_bytes;
+   pos.x = x;
+   pos.y = y;
+   pos.z = 0;
    if (mm->input.geometry) {
       unsigned char *bt  = mm->input.blocktype + v_off;
       unsigned char *geo = mm->input.geometry + v_off;
@@ -3736,3 +3759,45 @@ int main(int argc, char **argv)
 // (v:  x:2,y:2,z:2,light:2)
 
 #endif // STB_VOXEL_RENDER_IMPLEMENTATION
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
+software, either in source code form or as a compiled binary, for any purpose, 
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this 
+software dedicate any and all copyright interest in the software to the public 
+domain. We make this dedication for the benefit of the public at large and to 
+the detriment of our heirs and successors. We intend this dedication to be an 
+overt act of relinquishment in perpetuity of all present and future rights to 
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/

+ 50 - 3
stretchy_buffer.h

@@ -2,7 +2,7 @@
 // a vector<>-like dynamic array for C
 //
 // version history:
-//      1.02 -  compiles as C++, but untested
+//      1.02 -  tweaks to syntax for no good reason
 //      1.01 -  added a "common uses" documentation section
 //      1.0  -  fixed bug in the version I posted prematurely
 //      0.9  -  rewrite to try to avoid strict-aliasing optimization
@@ -21,7 +21,7 @@
 //        - the length of the "in-use" part of the array
 //        - the current size of the allocated array
 //
-//    I find it to be single most useful non-built-in-structure when
+//    I find it to be the single most useful non-built-in-structure when
 //    programming in C (hash tables a close second), but to be clear
 //    it lacks many of the capabilities of C++ vector<>: there is no
 //    range checking, the object address isn't stable (see next section
@@ -57,7 +57,7 @@
 //
 //         1. can't take long-term pointers to elements of the array
 //         2. have to return the pointer from functions which might expand it
-//            (either as a return value or by passing it back)
+//            (either as a return value or by storing it to a ptr-to-ptr)
 //
 //    Now you can do the following things with this array:
 //
@@ -161,6 +161,10 @@
 //    The details are trivial and implementation is straightforward;
 //    the main trick is in realizing in the first place that it's
 //    possible to do this in a generic, type-safe way in C.
+//
+// LICENSE
+//
+//   See end of file for license information.
 
 #ifndef STB_STRETCHY_BUFFER_H_INCLUDED
 #define STB_STRETCHY_BUFFER_H_INCLUDED
@@ -208,3 +212,46 @@ static void * stb__sbgrowf(void *arr, int increment, int itemsize)
    }
 }
 #endif // STB_STRETCHY_BUFFER_H_INCLUDED
+
+
+/*
+------------------------------------------------------------------------------
+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.
+------------------------------------------------------------------------------
+*/

+ 7 - 0
tests/Makefile

@@ -0,0 +1,7 @@
+INCLUDES = -I..
+CFLAGS = -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -DSTB_DIVIDE_TEST
+CPPFLAGS = -Wno-write-strings -DSTB_DIVIDE_TEST
+
+all:
+	$(CC) $(INCLUDES) $(CFLAGS) ../stb_vorbis.c test_c_compilation.c     -lm
+	$(CC) $(INCLUDES) $(CPPFLAGS) test_cpp_compilation.cpp -lm

+ 47 - 0
tests/c_lexer_test.c

@@ -1,3 +1,50 @@
+#define STB_C_LEX_C_DECIMAL_INTS    Y   //  "0|[1-9][0-9]*"                        CLEX_intlit
+#define STB_C_LEX_C_HEX_INTS        Y   //  "0x[0-9a-fA-F]+"                       CLEX_intlit
+#define STB_C_LEX_C_OCTAL_INTS      Y   //  "[0-7]+"                               CLEX_intlit
+#define STB_C_LEX_C_DECIMAL_FLOATS  Y   //  "[0-9]*(.[0-9]*([eE][-+]?[0-9]+)?)     CLEX_floatlit
+#define STB_C_LEX_C99_HEX_FLOATS    Y   //  "0x{hex}+(.{hex}*)?[pP][-+]?{hex}+     CLEX_floatlit
+#define STB_C_LEX_C_IDENTIFIERS     Y   //  "[_a-zA-Z][_a-zA-Z0-9]*"               CLEX_id
+#define STB_C_LEX_C_DQ_STRINGS      Y   //  double-quote-delimited strings with escapes  CLEX_dqstring
+#define STB_C_LEX_C_SQ_STRINGS      Y   //  single-quote-delimited strings with escapes  CLEX_ssstring
+#define STB_C_LEX_C_CHARS           Y   //  single-quote-delimited character with escape CLEX_charlits
+#define STB_C_LEX_C_COMMENTS        Y   //  "/* comment */"
+#define STB_C_LEX_CPP_COMMENTS      Y   //  "// comment to end of line\n"
+#define STB_C_LEX_C_COMPARISONS     Y   //  "==" CLEX_eq  "!=" CLEX_noteq   "<=" CLEX_lesseq  ">=" CLEX_greatereq
+#define STB_C_LEX_C_LOGICAL         Y   //  "&&"  CLEX_andand   "||"  CLEX_oror
+#define STB_C_LEX_C_SHIFTS          Y   //  "<<"  CLEX_shl      ">>"  CLEX_shr
+#define STB_C_LEX_C_INCREMENTS      Y   //  "++"  CLEX_plusplus "--"  CLEX_minusminus
+#define STB_C_LEX_C_ARROW           Y   //  "->"  CLEX_arrow
+#define STB_C_LEX_EQUAL_ARROW       Y   //  "=>"  CLEX_eqarrow
+#define STB_C_LEX_C_BITWISEEQ       Y   //  "&="  CLEX_andeq    "|="  CLEX_oreq     "^="  CLEX_xoreq
+#define STB_C_LEX_C_ARITHEQ         Y   //  "+="  CLEX_pluseq   "-="  CLEX_minuseq
+                                        //  "*="  CLEX_muleq    "/="  CLEX_diveq    "%=" CLEX_modeq
+                                        //  if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ:
+                                        //                      "<<=" CLEX_shleq    ">>=" CLEX_shreq
+
+#define STB_C_LEX_PARSE_SUFFIXES    Y   // letters after numbers are parsed as part of those numbers, and must be in suffix list below
+#define STB_C_LEX_DECIMAL_SUFFIXES  "uUlL"  // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage
+#define STB_C_LEX_HEX_SUFFIXES      "lL"  // e.g. "uUlL"
+#define STB_C_LEX_OCTAL_SUFFIXES    "lL"  // e.g. "uUlL"
+#define STB_C_LEX_FLOAT_SUFFIXES    "uulL"  //
+
+#define STB_C_LEX_0_IS_EOF             N  // if Y, ends parsing at '\0'; if N, returns '\0' as token
+#define STB_C_LEX_INTEGERS_AS_DOUBLES  N  // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N
+#define STB_C_LEX_MULTILINE_DSTRINGS   Y  // allow newlines in double-quoted strings
+#define STB_C_LEX_MULTILINE_SSTRINGS   Y  // allow newlines in single-quoted strings
+#define STB_C_LEX_USE_STDLIB           N  // use strtod,strtol for parsing #s; otherwise inaccurate hack
+#define STB_C_LEX_DOLLAR_IDENTIFIER    Y  // allow $ as an identifier character
+#define STB_C_LEX_FLOAT_NO_DECIMAL     Y  // allow floats that have no decimal point if they have an exponent
+
+#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES  Y   // if Y, all CLEX_ token names are defined, even if never returned
+                                              // leaving it as N should help you catch config bugs
+
+#define STB_C_LEX_DISCARD_PREPROCESSOR    Y   // discard C-preprocessor directives (e.g. after prepocess
+                                              // still have #line, #pragma, etc)
+
+#define STB_C_LEXER_DEFINITIONS         // This line prevents the header file from replacing your definitions
+
+
+
 #define STB_C_LEXER_IMPLEMENTATION
 #define STB_C_LEXER_SELF_TEST
 #include "../stb_c_lexer.h"

+ 1 - 1
tests/caveview/cave_render.c

@@ -699,7 +699,7 @@ static int test_plane(plane *p, float x0, float y0, float z0, float x1, float y1
 static int is_box_in_frustum(float *bmin, float *bmax)
 {
    int i;
-   for (i=0; i < 5; ++i)
+   for (i=0; i < 6; ++i)
       if (!test_plane(&frustum[i], bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2]))
          return 0;
    return 1;

+ 363 - 0
tests/grid_reachability.c

@@ -0,0 +1,363 @@
+#define STB_CONNECTED_COMPONENTS_IMPLEMENTATION
+#define STBCC_GRID_COUNT_X_LOG2  10
+#define STBCC_GRID_COUNT_Y_LOG2  10
+#include "stb_connected_components.h"
+
+#ifdef GRID_TEST
+
+#include <windows.h>
+#include <stdio.h>
+#include <direct.h>
+
+//#define STB_DEFINE
+#include "stb.h"
+
+//#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+
+//#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "stb_image_write.h"
+
+typedef struct
+{
+   uint16 x,y;
+} point;
+
+point leader[1024][1024];
+uint32 color[1024][1024];
+
+point find(int x, int y)
+{
+   point p,q;
+   p = leader[y][x];
+   if (p.x == x && p.y == y)
+      return p;
+   q = find(p.x, p.y);
+   leader[y][x] = q;
+   return q;
+}
+
+void onion(int x1, int y1, int x2, int y2)
+{
+   point p = find(x1,y1);
+   point q = find(x2,y2);
+
+   if (p.x == q.x && p.y == q.y)
+      return;
+
+   leader[p.y][p.x] = q;
+}
+
+void reference(uint8 *map, int w, int h)
+{
+   int i,j;
+
+   for (j=0; j < h; ++j) {
+      for (i=0; i < w; ++i) {
+         leader[j][i].x = i;
+         leader[j][i].y = j;
+      }
+   }
+         
+   for (j=1; j < h-1; ++j) {
+      for (i=1; i < w-1; ++i) {
+         if (map[j*w+i] == 255) {
+            if (map[(j+1)*w+i] == 255) onion(i,j, i,j+1);
+            if (map[(j)*w+i+1] == 255) onion(i,j, i+1,j);
+         }
+      }
+   }
+
+   for (j=0; j < h; ++j) {
+      for (i=0; i < w; ++i) {
+         uint32 c = 0xff000000;
+         if (leader[j][i].x == i && leader[j][i].y == j) {
+            if (map[j*w+i] == 255)
+               c = stb_randLCG() | 0xff404040;
+         }
+         color[j][i] = c;
+      }
+   }
+
+   for (j=0; j < h; ++j) {
+      for (i=0; i < w; ++i) {
+         if (leader[j][i].x != i || leader[j][i].y != j) {
+            point p = find(i,j);
+            color[j][i] = color[p.y][p.x];
+         }
+      }
+   }
+}
+
+void write_map(stbcc_grid *g, int w, int h, char *filename)
+{
+   int i,j;
+   for (j=0; j < h; ++j) {
+      for (i=0; i < w; ++i) {
+         unsigned int c;
+         c = stbcc_get_unique_id(g,i,j);
+         c = stb_rehash_improved(c)&0xffffff;
+         if (c == STBCC_NULL_UNIQUE_ID)
+            c = 0xff000000;
+         else
+            c = (~c)^0x555555;
+         if (i % 32 == 0 || j %32 == 0) {
+            int r = (c >> 16) & 255;
+            int g = (c >> 8) & 255;
+            int b = c & 255;
+            r = (r+130)/2;
+            g = (g+130)/2;
+            b = (b+130)/2;
+            c = 0xff000000 + (r<<16) + (g<<8) + b;
+         }
+         color[j][i] = c;
+      }
+   }
+   stbi_write_png(filename, w, h, 4, color, 4*w);
+}
+
+void test_connected(stbcc_grid *g)
+{
+   int n = stbcc_query_grid_node_connection(g, 512, 90, 512, 871);
+   //printf("%d ", n);
+}
+
+static char *message;
+LARGE_INTEGER start;
+
+void start_timer(char *s)
+{
+   message = s;
+   QueryPerformanceCounter(&start);
+}
+
+void end_timer(void)
+{
+   LARGE_INTEGER end, freq;
+   double tm;
+
+   QueryPerformanceCounter(&end);
+   QueryPerformanceFrequency(&freq);
+
+   tm = (end.QuadPart - start.QuadPart) / (double) freq.QuadPart;
+   printf("%6.4lf ms: %s\n", tm * 1000, message);
+}
+
+extern void quicktest(void);
+
+int loc[5000][2];
+int main(int argc, char **argv)
+{
+   stbcc_grid *g;
+
+   int w,h, i,j,k=0, count=0, r;
+   uint8 *map = stbi_load("data/map_03.png", &w, &h, 0, 1);
+
+   assert(map);
+   quicktest();
+
+   for (j=0; j < h; ++j)
+      for (i=0; i < w; ++i)
+         map[j*w+i] = ~map[j*w+i];
+
+   for (i=0; i < w; ++i)
+      for (j=0; j < h; ++j)
+         //map[j*w+i] = (((i+1) ^ (j+1)) >> 1) & 1 ? 255 : 0;
+         map[j*w+i] = stb_max(abs(i-w/2),abs(j-h/2)) & 1 ? 255 : 0;
+         //map[j*w+i] = (((i ^ j) >> 5) ^ (i ^ j)) & 1 ? 255 : 0;
+         //map[j*w+i] = stb_rand() & 1 ? 255 : 0;
+
+   #if 1
+   for (i=0; i < 100000; ++i)
+      map[(stb_rand()%h)*w + stb_rand()%w] ^= 255;
+   #endif
+            
+   _mkdir("tests/output/stbcc");
+
+   stbi_write_png("tests/output/stbcc/reference.png", w, h, 1, map, 0);
+
+   //reference(map, w, h);
+
+   g = malloc(stbcc_grid_sizeof());
+   printf("Size: %d\n", stbcc_grid_sizeof());
+
+#if 0
+   memset(map, 0, w*h);
+   stbcc_init_grid(g, map, w, h);
+   {
+      int n;
+      char **s = stb_stringfile("c:/x/clockwork_update.txt", &n);
+      write_map(g, w, h, "tests/output/stbcc/base.png");
+      for (i=1; i < n; i += 1) {
+         int x,y,t;
+         sscanf(s[i], "%d %d %d", &x, &y, &t);
+         if (i == 571678)
+            write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_good.png", i));
+         stbcc_update_grid(g, x, y, t);
+         if (i == 571678)
+            write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_bad.png", i));
+         //if (i > 571648 && i <= 571712)
+            //write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_%06d.png", i));
+      }
+      write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_%06d.png", i-1));
+   }
+   return 0;
+#endif
+
+
+   start_timer("init");
+   stbcc_init_grid(g, map, w, h);
+   end_timer();
+   //g = stb_file("c:/x/clockwork_path.bin", 0);
+   write_map(g, w, h, "tests/output/stbcc/base.png");
+
+   for (i=0; i < 5000;) {
+      loc[i][0] = stb_rand() % w;
+      loc[i][1] = stb_rand() % h;
+      if (stbcc_query_grid_open(g, loc[i][0], loc[i][1]))
+         ++i;
+   }
+
+   r = 0;
+   start_timer("reachable");
+   for (i=0; i < 2000; ++i) {
+      for (j=0; j < 2000; ++j) {
+         int x1 = loc[i][0], y1 = loc[i][1];
+         int x2 = loc[2000+j][0], y2 = loc[2000+j][1];
+         r += stbcc_query_grid_node_connection(g, x1,y1, x2,y2);
+      }
+   }
+   end_timer();
+   printf("%d reachable\n", r);
+
+   printf("Cluster size: %d,%d\n", STBCC__CLUSTER_SIZE_X, STBCC__CLUSTER_SIZE_Y);
+
+   #if 1
+   for (j=0; j < 10; ++j) {
+      for (i=0; i < 5000; ++i) {
+         loc[i][0] = stb_rand() % w;
+         loc[i][1] = stb_rand() % h;
+      }
+      start_timer("updating 2500");
+      for (i=0; i < 2500; ++i) {
+         if (stbcc_query_grid_open(g, loc[i][0], loc[i][1]))
+            stbcc_update_grid(g, loc[i][0], loc[i][1], 1);
+         else
+            stbcc_update_grid(g, loc[i][0], loc[i][1], 0);
+      }
+      end_timer();
+      write_map(g, w, h, stb_sprintf("tests/output/stbcc/update_random_%d.png", j*i));
+   }
+   #endif
+
+   #if 0
+   start_timer("removing");
+   count = 0;
+   for (i=0; i < 1800; ++i) {
+      int x,y,a,b;
+      x = stb_rand() % (w-32);
+      y = stb_rand() % (h-32);
+      
+      if (i & 1) {
+         for (a=0; a < 32; ++a)
+            for (b=0; b < 1; ++b)
+               if (stbcc_query_grid_open(g, x+a, y+b)) {
+                  stbcc_update_grid(g, x+a, y+b, 1);
+                  ++count;
+               }
+      } else {
+         for (a=0; a < 1; ++a)
+            for (b=0; b < 32; ++b)
+               if (stbcc_query_grid_open(g, x+a, y+b)) {
+                  stbcc_update_grid(g, x+a, y+b, 1);
+                  ++count;
+               }
+      }
+
+      //if (i % 100 == 0) write_map(g, w, h, stb_sprintf("tests/output/stbcc/open_random_%d.png", i+1));
+   }
+   end_timer();
+   printf("Removed %d grid spaces\n", count);
+   write_map(g, w, h, stb_sprintf("tests/output/stbcc/open_random_%d.png", i));
+
+
+   r = 0;
+   start_timer("reachable");
+   for (i=0; i < 1000; ++i) {
+      for (j=0; j < 1000; ++j) {
+         int x1 = loc[i][0], y1 = loc[i][1];
+         int x2 = loc[j][0], y2 = loc[j][1];
+         r += stbcc_query_grid_node_connection(g, x1,y1, x2,y2);
+      }
+   }
+   end_timer();
+   printf("%d reachable\n", r);
+
+   start_timer("adding");
+   count = 0;
+   for (i=0; i < 1800; ++i) {
+      int x,y,a,b;
+      x = stb_rand() % (w-32);
+      y = stb_rand() % (h-32);
+
+      if (i & 1) {
+         for (a=0; a < 32; ++a)
+            for (b=0; b < 1; ++b)
+               if (!stbcc_query_grid_open(g, x+a, y+b)) {
+                  stbcc_update_grid(g, x+a, y+b, 0);
+                  ++count;
+               }
+      } else {
+         for (a=0; a < 1; ++a)
+            for (b=0; b < 32; ++b)
+               if (!stbcc_query_grid_open(g, x+a, y+b)) {
+                  stbcc_update_grid(g, x+a, y+b, 0);
+                  ++count;
+               }
+      }
+
+      //if (i % 100 == 0) write_map(g, w, h, stb_sprintf("tests/output/stbcc/close_random_%d.png", i+1));
+   }
+   end_timer();
+   write_map(g, w, h, stb_sprintf("tests/output/stbcc/close_random_%d.png", i));
+   printf("Added %d grid spaces\n", count);
+   #endif
+
+
+   #if 0  // for map_02.png
+   start_timer("process");
+   for (k=0; k < 20; ++k) {
+      for (j=0; j < h; ++j) {
+         int any=0;
+         for (i=0; i < w; ++i) {
+            if (map[j*w+i] > 10 && map[j*w+i] < 250) {
+               //start_timer(stb_sprintf("open %d,%d", i,j));
+               stbcc_update_grid(g, i, j, 0);
+               test_connected(g);
+               //end_timer();
+               any = 1;
+            }
+         }
+         if (any) write_map(g, w, h, stb_sprintf("tests/output/stbcc/open_row_%04d.png", j));
+      }
+
+      for (j=0; j < h; ++j) {
+         int any=0;
+         for (i=0; i < w; ++i) {
+            if (map[j*w+i] > 10 && map[j*w+i] < 250) {
+               //start_timer(stb_sprintf("close %d,%d", i,j));
+               stbcc_update_grid(g, i, j, 1);
+               test_connected(g);
+               //end_timer();
+               any = 1;
+            }
+         }
+         if (any) write_map(g, w, h, stb_sprintf("tests/output/stbcc/close_row_%04d.png", j));
+      }
+   }
+   end_timer();
+   #endif
+
+   return 0;
+}
+#endif

+ 19 - 2
tests/image_test.c

@@ -7,7 +7,7 @@
 #define STB_DEFINE
 #include "stb.h"
 
-#define PNGSUITE_PRIMARY
+//#define PNGSUITE_PRIMARY
 
 #if 0
 void test_ycbcr(void)
@@ -53,6 +53,13 @@ void test_ycbcr(void)
 
 float hdr_data[200][200][3];
 
+void dummy_write(void *context, void *data, int len)
+{
+   static char dummy[1024];
+   if (len > 1024) len = 1024;
+   memcpy(dummy, data, len);
+}
+
 int main(int argc, char **argv)
 {
    int w,h;
@@ -73,18 +80,28 @@ int main(int argc, char **argv)
       int i, n;
 
       for (i=1; i < argc; ++i) {
+         int res;
+         int w2,h2,n2;
          unsigned char *data;
          printf("%s\n", argv[i]);
+         res = stbi_info(argv[1], &w2, &h2, &n2);
          data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
          data = stbi_load(argv[i], &w, &h,  0, 1); if (data) free(data); else printf("Failed 1\n");
          data = stbi_load(argv[i], &w, &h,  0, 2); if (data) free(data); else printf("Failed 2\n");
          data = stbi_load(argv[i], &w, &h,  0, 3); if (data) free(data); else printf("Failed 3\n");
-         data = stbi_load(argv[i], &w, &h,  0, 4);
+         data = stbi_load(argv[i], &w, &h, &n, 4);
          assert(data);
+         assert(w == w2 && h == h2 && n == n2);
+         assert(res);
          if (data) {
             char fname[512];
             stb_splitpath(fname, argv[i], STB_FILE);
             stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4);
+            stbi_write_bmp(stb_sprintf("output/%s.bmp", fname), w, h, 4, data);
+            stbi_write_tga(stb_sprintf("output/%s.tga", fname), w, h, 4, data);
+            stbi_write_png_to_func(dummy_write,0, w, h, 4, data, w*4);
+            stbi_write_bmp_to_func(dummy_write,0, w, h, 4, data);
+            stbi_write_tga_to_func(dummy_write,0, w, h, 4, data);
             free(data);
          } else
             printf("FAILED 4\n");

+ 2 - 0
tests/oversample/stb_wingraph.h

@@ -25,6 +25,8 @@
    #pragma comment(lib, "opengl32.lib")
    #pragma comment(lib, "glu32.lib")
    #pragma comment(lib, "winmm.lib")
+   #pragma comment(lib, "gdi32.lib")
+   #pragma comment(lib, "user32.lib")
 #endif
 
 #ifdef __cplusplus

+ 124 - 0
tests/pg_test/pg_test.c

@@ -0,0 +1,124 @@
+#define STB_DEFINE
+#include "stb.h"
+#define STB_PG_IMPLEMENTATION
+#include "stb_pg.h"
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "stb_image_write.h"
+
+static float *hf;
+static int hf_width = 10001;
+static int hf_height = 10001;
+
+static float get_height(float x, float y)
+{
+   float h00,h01,h10,h11,h0,h1;
+   int ix,iy;
+   if (x < 0) x = 0;
+   if (x > hf_width-1) x = (float) hf_width-1;
+   if (y < 0) y = 0;
+   if (y > hf_height-1) y = (float) hf_height-1;
+   ix = (int) x; x -= ix;
+   iy = (int) y; y -= iy;
+   h00 = hf[(iy+0)*hf_height+(ix+0)];
+   h10 = hf[(iy+0)*hf_height+(ix+1)];
+   h01 = hf[(iy+1)*hf_height+(ix+0)];
+   h11 = hf[(iy+1)*hf_height+(ix+1)];
+   h0 = stb_lerp(y, h00, h01);
+   h1 = stb_lerp(y, h10, h11);
+   return stb_lerp(x, h0, h1);
+}
+
+void stbpg_tick(float dt)
+{
+   int i=0,j=0;
+   int step = 1;
+
+   glUseProgram(0);
+
+   glClearColor(0.6f,0.7f,1.0f,1.0f);
+   glClearDepth(1.0f);
+   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+   glDepthFunc(GL_LESS);
+   glEnable(GL_DEPTH_TEST);
+#if 1
+   glEnable(GL_CULL_FACE);
+
+   glMatrixMode(GL_PROJECTION);
+   glLoadIdentity();
+   gluPerspective(60.0, 1920/1080.0f, 0.02f, 8000.0f);
+   //glOrtho(-8,8,-6,6, -100, 100);
+
+   glMatrixMode(GL_MODELVIEW);
+   glLoadIdentity();
+   glRotatef(-90, 1,0,0); // z-up
+
+   {
+      float x,y;
+      stbpg_get_mouselook(&x,&y);
+      glRotatef(-y, 1,0,0);
+      glRotatef(-x, 0,0,1);
+   }
+
+   {
+      static float cam_x = 1000;
+      static float cam_y = 1000;
+      static float cam_z = 700;
+      float x=0,y=0;
+      stbpg_get_keymove(&x,&y);
+      cam_x += x*dt*5.0f;
+      cam_y += y*dt*5.0f;
+      glTranslatef(-cam_x, -cam_y, -cam_z);
+      if (cam_x >= 0 && cam_x < hf_width && cam_y >= 0 && cam_y < hf_height)
+         cam_z = get_height(cam_x, cam_y) + 1.65f; // average eye height in meters
+   }
+
+   for (j=501; j+1 < 1500+0*hf_height; j += step) {
+      glBegin(GL_QUAD_STRIP);
+      for (i=501; i < 1500+0*hf_width; i += step) {
+         static int flip=0;
+         if (flip)
+            glColor3f(0.5,0.5,0.5);
+         else
+            glColor3f(0.4f,0.4f,0.4f);
+         flip = !flip;
+         glVertex3f((float) i, (float) j+step,hf[(j+step)*hf_width+i]);
+         glVertex3f((float) i, (float) j   ,hf[  j    *hf_width+i]);
+      }
+      glEnd();
+   }
+
+   glBegin(GL_LINES);
+      glColor3f(1,0,0); glVertex3f(10,0,0); glVertex3f(0,0,0);
+      glColor3f(0,1,0); glVertex3f(0,10,0); glVertex3f(0,0,0);
+      glColor3f(0,0,1); glVertex3f(0,0,10); glVertex3f(0,0,0);
+   glEnd();
+#endif
+}
+
+void stbpg_main(int argc, char **argv)
+{
+   int i,j;
+
+   #if 0
+   int w,h,c;
+   unsigned short *data = stbi_load_16("c:/x/ned_1m/test2.png", &w, &h, &c, 1);
+   stb_filewrite("c:/x/ned_1m/x73_y428_10012_10012.bin", data, w*h*2);
+   #else
+   unsigned short *data = stb_file("c:/x/ned_1m/x73_y428_10012_10012.bin", NULL);
+   int w=10012, h = 10012;
+   #endif
+
+   hf = malloc(hf_width * hf_height * 4);
+   for (j=0; j < hf_height; ++j)
+      for (i=0; i < hf_width; ++i)
+         hf[j*hf_width+i] = data[j*w+i] / 32.0f;
+
+   stbpg_gl_compat_version(1,1);   
+   stbpg_windowed("terrain_edit", 1920, 1080);
+   stbpg_run();
+
+   return;
+}

+ 155 - 20
tests/resample_test.cpp

@@ -1,4 +1,5 @@
-#include <malloc.h>
+#include <stdlib.h>
+#include <stdio.h>
 
 #if defined(_WIN32) && _MSC_VER > 1200
 #define STBIR_ASSERT(x) \
@@ -69,9 +70,11 @@ void stbir_progress(float p)
 
 #ifdef _WIN32
 #include <sys/timeb.h>
-#endif
-
 #include <direct.h>
+#define mkdir(a, b) _mkdir(a)
+#else
+#include <sys/stat.h>
+#endif
 
 #define MT_SIZE 624
 static size_t g_aiMT[MT_SIZE];
@@ -356,6 +359,54 @@ void test_subpixel(const char* file, float width_percent, float height_percent,
 	free(output_data);
 }
 
+void test_subpixel_region(const char* file, float width_percent, float height_percent, float s0, float t0, float s1, float t1)
+{
+	int w, h, n;
+	unsigned char* input_data = stbi_load(file, &w, &h, &n, 0);
+
+	if (input_data == NULL)
+		return;
+
+	int new_w = (int)(w * width_percent);
+	int new_h = (int)(h * height_percent);
+
+	unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char));
+
+	stbir_resize_region(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, s0, t0, s1, t1);
+
+	stbi_image_free(input_data);
+
+	char output[200];
+	sprintf(output, "test-output/subpixel-region-%d-%d-%f-%f-%f-%f-%s", new_w, new_h, s0, t0, s1, t1, file);
+	stbi_write_png(output, new_w, new_h, n, output_data, 0);
+
+	free(output_data);
+}
+
+void test_subpixel_command(const char* file, float width_percent, float height_percent, float x_scale, float y_scale, float x_offset, float y_offset)
+{
+	int w, h, n;
+	unsigned char* input_data = stbi_load(file, &w, &h, &n, 0);
+
+	if (input_data == NULL)
+		return;
+
+	int new_w = (int)(w * width_percent);
+	int new_h = (int)(h * height_percent);
+
+	unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char));
+
+	stbir_resize_subpixel(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, x_scale, y_scale, x_offset, y_offset);
+
+	stbi_image_free(input_data);
+
+	char output[200];
+	sprintf(output, "test-output/subpixel-command-%d-%d-%f-%f-%f-%f-%s", new_w, new_h, x_scale, y_scale, x_offset, y_offset, file);
+	stbi_write_png(output, new_w, new_h, n, output_data, 0);
+
+	free(output_data);
+}
+
 unsigned int* pixel(unsigned int* buffer, int x, int y, int c, int w, int n)
 {
 	return &buffer[y*w*n + x*n + c];
@@ -461,6 +512,18 @@ void test_subpixel_1()
 			STBIR_ASSERT(output_data[y * 16 + x + 8] == output_right[y * 8 + x]);
 		}
 	}
+
+	stbir_resize_subpixel(image, 8, 8, 0, output_left, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 2, 2, 0, 0);
+	stbir_resize_subpixel(image, 8, 8, 0, output_right, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 2, 2, 8, 0);
+
+	{for (int x = 0; x < 8; x++)
+	{
+		for (int y = 0; y < 16; y++)
+		{
+			STBIR_ASSERT(output_data[y * 16 + x] == output_left[y * 8 + x]);
+			STBIR_ASSERT(output_data[y * 16 + x + 8] == output_right[y * 8 + x]);
+		}
+	}}
 }
 
 // test that replicating an image and using a subtile of it produces same results as wraparound
@@ -498,6 +561,14 @@ void test_subpixel_2()
 		for (int y = 0; y < 16; y++)
 			STBIR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]);
 	}}
+
+	stbir_resize_subpixel(large_image, 32, 32, 0, output_data_2, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 2, 2, 16, 16);
+
+	{for (int x = 0; x < 16; x++)
+	{
+		for (int y = 0; y < 16; y++)
+			STBIR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]);
+	}}
 }
 
 // test that 0,0,1,1 subpixel produces same result as no-rect
@@ -521,6 +592,14 @@ void test_subpixel_3()
 		for (int y = 0; y < 32; y++)
 			STBIR_ASSERT(output_data_1[y * 32 + x] == output_data_2[y * 32 + x]);
 	}
+
+	stbir_resize_subpixel(image, 8, 8, 0, output_data_1, 32, 32, 0, STBIR_TYPE_UINT8, 1, 0, STBIR_ALPHA_CHANNEL_NONE, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_LINEAR, NULL, 4, 4, 0, 0);
+
+	{for (int x = 0; x < 32; x++)
+	{
+		for (int y = 0; y < 32; y++)
+			STBIR_ASSERT(output_data_1[y * 32 + x] == output_data_2[y * 32 + x]);
+	}}
 }
 
 // test that 1:1 resample using s,t=0,0,1,1 with bilinear produces original image
@@ -537,6 +616,9 @@ void test_subpixel_4()
 
 	stbir_resize_region(image, 8, 8, 0, output, 8, 8, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, &g_context, 0, 0, 1, 1);
 	STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0);
+
+	stbir_resize_subpixel(image, 8, 8, 0, output, 8, 8, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, &g_context, 1, 1, 0, 0);
+	STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0);
 }
 
 static unsigned int  image88_int[8][8];
@@ -753,7 +835,7 @@ void test_filters(void)
 
 #define UMAX32   4294967295U
 
-static void write32(char *filename, stbir_uint32 *output, int w, int h)
+static void write32(const char *filename, stbir_uint32 *output, int w, int h)
 {
     stbir_uint8 *data = (stbir_uint8*) malloc(w*h*3);
     for (int i=0; i < w*h*3; ++i)
@@ -789,9 +871,9 @@ static void test_32(void)
 void test_suite(int argc, char **argv)
 {
 	int i;
-	char *barbara;
+	const char *barbara;
 
-	_mkdir("test-output");
+	mkdir("test-output", 777);
 
 	if (argc > 1)
 		barbara = argv[1];
@@ -880,17 +962,70 @@ void test_suite(int argc, char **argv)
 	stbir_resize(image88, 8, 8, 0, output88, 16, 4, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context);
 	stbir_resize(image88, 8, 8, 0, output88, 16, 4, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, &g_context);
 
-	for (i = 0; i < 10; i++)
+	int barbara_width, barbara_height, barbara_channels;
+	stbi_image_free(stbi_load(barbara, &barbara_width, &barbara_height, &barbara_channels, 0));
+
+	int res = 10;
+	// Downscaling
+	{for (int i = 0; i <= res; i++)
+	{
+		float t = (float)i/res;
+		float scale = 0.5;
+		float out_scale = 2.0f/3;
+		float x_shift = (barbara_width*out_scale - barbara_width*scale) * t;
+		float y_shift = (barbara_height*out_scale - barbara_height*scale) * t;
+
+		test_subpixel_command(barbara, scale, scale, out_scale, out_scale, x_shift, y_shift);
+	}}
+
+	// Upscaling
+	{for (int i = 0; i <= res; i++)
+	{
+		float t = (float)i/res;
+		float scale = 2;
+		float out_scale = 3;
+		float x_shift = (barbara_width*out_scale - barbara_width*scale) * t;
+		float y_shift = (barbara_height*out_scale - barbara_height*scale) * t;
+
+		test_subpixel_command(barbara, scale, scale, out_scale, out_scale, x_shift, y_shift);
+	}}
+
+	// Downscaling
+	{for (int i = 0; i <= res; i++)
+	{
+		float t = (float)i/res / 2;
+		test_subpixel_region(barbara, 0.25f, 0.25f, t, t, t+0.5f, t+0.5f);
+	}}
+
+	// No scaling
+	{for (int i = 0; i <= res; i++)
+	{
+		float t = (float)i/res / 2;
+		test_subpixel_region(barbara, 0.5f, 0.5f, t, t, t+0.5f, t+0.5f);
+	}}
+
+	// Upscaling
+	{for (int i = 0; i <= res; i++)
+	{
+		float t = (float)i/res / 2;
+		test_subpixel_region(barbara, 1, 1, t, t, t+0.5f, t+0.5f);
+	}}
+
+	{for (i = 0; i < 10; i++)
 		test_subpixel(barbara, 0.5f, 0.5f, (float)i / 10, 1);
+   }
 
-	for (i = 0; i < 10; i++)
+	{for (i = 0; i < 10; i++)
 		test_subpixel(barbara, 0.5f, 0.5f, 1, (float)i / 10);
+   }
 
-	for (i = 0; i < 10; i++)
+	{for (i = 0; i < 10; i++)
 		test_subpixel(barbara, 2, 2, (float)i / 10, 1);
+   }
 
-	for (i = 0; i < 10; i++)
+	{for (i = 0; i < 10; i++)
 		test_subpixel(barbara, 2, 2, 1, (float)i / 10);
+   }
 
 	// Channels test
 	test_channels(barbara, 0.5f, 0.5f, 1);
@@ -916,40 +1051,40 @@ void test_suite(int argc, char **argv)
 	resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CATMULLROM  , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png");
 	resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_MITCHELL    , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png");
 
-	for (i = 10; i < 100; i++)
+	{for (i = 10; i < 100; i++)
 	{
 		char outname[200];
 		sprintf(outname, "test-output/barbara-width-%d.jpg", i);
 		resize_image(barbara, (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname);
-	}
+	}}
 
-	for (i = 110; i < 500; i += 10)
+	{for (i = 110; i < 500; i += 10)
 	{
 		char outname[200];
 		sprintf(outname, "test-output/barbara-width-%d.jpg", i);
 		resize_image(barbara, (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname);
-	}
+	}}
 
-	for (i = 10; i < 100; i++)
+	{for (i = 10; i < 100; i++)
 	{
 		char outname[200];
 		sprintf(outname, "test-output/barbara-height-%d.jpg", i);
 		resize_image(barbara, 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname);
-	}
+	}}
 
-	for (i = 110; i < 500; i += 10)
+	{for (i = 110; i < 500; i += 10)
 	{
 		char outname[200];
 		sprintf(outname, "test-output/barbara-height-%d.jpg", i);
 		resize_image(barbara, 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname);
-	}
+	}}
 
-	for (i = 50; i < 200; i += 10)
+	{for (i = 50; i < 200; i += 10)
 	{
 		char outname[200];
 		sprintf(outname, "test-output/barbara-width-height-%d.jpg", i);
 		resize_image(barbara, 100 / (float)i, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname);
-	}
+	}}
 
 	test_format<unsigned short>(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB);
 	test_format<unsigned short>(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR);

+ 3 - 0
tests/resample_test_c.c

@@ -3,3 +3,6 @@
 #include "stb_image_resize.h"
 
 // Just to make sure it will build properly with a c compiler
+
+int main() {
+}

+ 26 - 2
tests/stb.dsp

@@ -42,7 +42,7 @@ RSC=rc.exe
 # PROP Ignore_Export_Lib 0
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /G6 /MT /W3 /GX /Z7 /O2 /Ob2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "TT_TEST" /FD /c
+# ADD CPP /nologo /G6 /MT /W3 /GX /Z7 /O2 /Ob2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "GRID_TEST" /FD /c
 # ADD BASE RSC /l 0x409 /d "NDEBUG"
 # ADD RSC /l 0x409 /d "NDEBUG"
 BSC32=bscmake.exe
@@ -66,7 +66,7 @@ LINK32=link.exe
 # PROP Ignore_Export_Lib 0
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "VORBIS_TEST" /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "GRID_TEST" /FR /FD /GZ /c
 # SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
 # ADD RSC /l 0x409 /d "_DEBUG"
@@ -86,6 +86,10 @@ LINK32=link.exe
 # Name "stb - Win32 Debug"
 # Begin Source File
 
+SOURCE=.\grid_reachability.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\stb.c
 # End Source File
 # Begin Source File
@@ -98,6 +102,10 @@ SOURCE=..\stb_c_lexer.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\stb_connected_components.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\stb_divide.h
 # End Source File
 # Begin Source File
@@ -106,6 +114,10 @@ SOURCE=..\stb_dxt.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\stb_easy_font.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\stb_herringbone_wang_tile.h
 # End Source File
 # Begin Source File
@@ -126,14 +138,26 @@ SOURCE=..\stb_leakcheck.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\stb_malloc.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\stb_perlin.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\stb_pg.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\stb_rect_pack.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\stb_sprintf.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\stb_textedit.h
 # End Source File
 # Begin Source File

+ 37 - 1
tests/stb.dsw

@@ -63,7 +63,19 @@ Package=<4>
 
 ###############################################################################
 
-Project: "resize"=.\resize\resize.dsp - Package Owner=<4>
+Project: "pg_test"=.\pg_test\pg_test.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "resize"=.\resize.dsp - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -123,6 +135,30 @@ Package=<4>
 
 ###############################################################################
 
+Project: "unicode"=..\tools\unicode\unicode.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "vorbseek"=.\vorbseek\vorbseek.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
 Global:
 
 Package=<5>

+ 14 - 0
tests/test_c_compilation.c

@@ -1,3 +1,7 @@
+#include "stb_sprintf.h"
+#define STB_SPRINTF_IMPLEMENTATION
+#include "stb_sprintf.h"
+
 #define STB_PERLIN_IMPLEMENTATION
 #define STB_IMAGE_WRITE_IMPLEMENTATION
 #define STB_DXT_IMPLEMENATION
@@ -8,7 +12,9 @@
 #define STB_IMAGE_RESIZE_IMPLEMENTATION
 #define STB_RECT_PACK_IMPLEMENTATION
 #define STB_VOXEL_RENDER_IMPLEMENTATION
+#define STB_EASY_FONT_IMPLEMENTATION
 
+#include "stb_easy_font.h"
 #include "stb_herringbone_wang_tile.h"
 #include "stb_image.h"
 #include "stb_image_write.h"
@@ -26,3 +32,11 @@
 #define STBTE_DRAW_TILE(x,y,id,highlight,data)  0
 #define STB_TILEMAP_EDITOR_IMPLEMENTATION
 #include "stb_tilemap_editor.h"
+
+
+int quicktest(void)
+{
+   char buffer[999];
+   stbsp_sprintf(buffer, "test%%test");
+   return 0;
+}

+ 9 - 0
tests/test_cpp_compilation.cpp

@@ -1,3 +1,7 @@
+#include "stb_sprintf.h"
+#define STB_SPRINTF_IMPLEMENTATION
+#include "stb_sprintf.h"
+
 #define STB_TRUETYPE_IMPLEMENTATION
 #define STB_PERLIN_IMPLEMENTATION
 #define STB_IMAGE_WRITE_IMPLEMENTATION
@@ -8,6 +12,7 @@
 #define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION
 #define STB_RECT_PACK_IMPLEMENTATION
 #define STB_VOXEL_RENDER_IMPLEMENTATION
+#define STB_CONNECTED_COMPONENTS_IMPLEMENTATION
 
 #define STBI_MALLOC     my_malloc
 #define STBI_FREE       my_free
@@ -27,6 +32,10 @@ void my_free(void *) { }
 #include "stb_divide.h"
 #include "stb_herringbone_wang_tile.h"
 
+#define STBCC_GRID_COUNT_X_LOG2  10
+#define STBCC_GRID_COUNT_Y_LOG2  10
+#include "stb_connected_components.h"
+
 #define STBVOX_CONFIG_MODE 1
 #include "stb_voxel_render.h"
 

+ 14 - 2
tests/test_truetype.c

@@ -3,13 +3,13 @@
 #include "stb_truetype.h"
 #include "stb_image_write.h"
 
+#ifdef TT_TEST
+
 #include <stdio.h>
 
 char ttf_buffer[1<<25];
 unsigned char output[512*100];
 
-#ifdef TT_TEST
-
 void debug(void)
 {
    stbtt_fontinfo font;
@@ -24,6 +24,7 @@ void debug(void)
 unsigned char temp_bitmap[BITMAP_H][BITMAP_W];
 stbtt_bakedchar cdata[256*2]; // ASCII 32..126 is 95 glyphs
 stbtt_packedchar pdata[256*2];
+
 int main(int argc, char **argv)
 {
    stbtt_fontinfo font;
@@ -35,6 +36,17 @@ int main(int argc, char **argv)
    // @TODO: why is minglui.ttc failing? 
    fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/mingliu.ttc", "rb"));
 
+   //fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/x/DroidSansMono.ttf", "rb"));
+   {
+      static stbtt_pack_context pc;
+      static stbtt_packedchar cd[256];
+      static unsigned char atlas[1024*1024];
+
+      stbtt_PackBegin(&pc, atlas, 1024,1024,1024,1,NULL);
+      stbtt_PackFontRange(&pc, ttf_buffer, 0, 32.0, 0, 256, cd);
+      stbtt_PackEnd(&pc);
+   }
+
 #if 0
    stbtt_BakeFontBitmap(ttf_buffer,stbtt_GetFontOffsetForIndex(ttf_buffer,0), 40.0, temp_bitmap[0],BITMAP_W,BITMAP_H, 32,96, cdata); // no guarantee this fits!
    stbi_write_png("fonttest1.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0);

+ 6 - 1
tests/test_vorbis.c

@@ -1,3 +1,7 @@
+#define STB_IMAGE_STATIC
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+
 #define STB_VORBIS_HEADER_ONLY
 #include "stb_vorbis.c"
 #include "stb.h"
@@ -8,10 +12,11 @@ extern void stb_vorbis_dumpmem(void);
 int main(int argc, char **argv)
 {
    size_t memlen;
-   unsigned char *mem = stb_fileu("c:/x/theme_03.ogg", &memlen);
+   unsigned char *mem = stb_fileu("c:/x/sketch008.ogg", &memlen);
    int chan, samplerate;
    short *output;
    int samples = stb_vorbis_decode_memory(mem, memlen, &chan, &samplerate, &output);
+   stb_filewrite("c:/x/sketch008.raw", output, samples*4);
    return 0;
 }
 #endif

+ 125 - 0
tests/vorbseek/vorbseek.c

@@ -0,0 +1,125 @@
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define STB_VORBIS_HEADER_ONLY
+#include "stb_vorbis.c"
+
+#define SAMPLES_TO_TEST 3000
+
+int test_count  [5] = { 5000, 3000, 2000, 50000, 50000 };
+int test_spacing[5] = {    1,  111, 3337,  7779, 72717 };
+
+int try_seeking(stb_vorbis *v, unsigned int pos, short *output, unsigned int num_samples)
+{
+   int count;
+   short samples[SAMPLES_TO_TEST*2];
+   assert(pos <= num_samples);
+
+   if (!stb_vorbis_seek(v, pos)) {
+      fprintf(stderr, "Seek to %u returned error from stb_vorbis\n", pos);
+      return 0;
+   }
+
+   count = stb_vorbis_get_samples_short_interleaved(v, 2, samples, SAMPLES_TO_TEST*2);
+
+   if (count > (int) (num_samples - pos)) {
+      fprintf(stderr, "Seek to %u allowed decoding %d samples when only %d should have been valid.\n",
+            pos, count, (int) (num_samples - pos));
+      return 0;
+   }
+
+   if (count < SAMPLES_TO_TEST && count < (int) (num_samples - pos)) {
+      fprintf(stderr, "Seek to %u only decoded %d samples of %d attempted when at least %d should have been valid.\n",
+         pos, count, SAMPLES_TO_TEST, num_samples - pos);
+      return 0;                      
+   }
+
+   if (0 != memcmp(samples, output + pos*2, count*2)) {
+      int k;
+      for (k=0; k < SAMPLES_TO_TEST*2; ++k) {
+         if (samples[k] != output[k]) {
+            fprintf(stderr, "Seek to %u produced incorrect samples starting at sample %u (short #%d in buffer).\n",
+                    pos, pos + (k/2), k);
+            break;
+         }
+      }
+      assert(k != SAMPLES_TO_TEST*2);
+      return 0;
+   }
+
+   return 1;
+}
+
+int main(int argc, char **argv)
+{
+   int num_chan, samprate;
+   int i, j, test, phase;
+   short *output;
+
+   if (argc == 1) {
+      fprintf(stderr, "Usage: vorbseek {vorbisfile} [{vorbisfile]*]\n");
+      fprintf(stderr, "Tests various seek offsets to make sure they're sample exact.\n");
+      return 0;
+   }
+
+   #if 0
+   {
+      // check that outofmem occurs correctly
+      stb_vorbis_alloc va;
+      va.alloc_buffer = malloc(1024*1024);
+      for (i=0; i < 1024*1024; i += 10) {
+         int error=0;
+         stb_vorbis *v;
+         va.alloc_buffer_length_in_bytes = i;
+         v = stb_vorbis_open_filename(argv[1], &error, &va);
+         if (v != NULL)
+            break;
+         printf("Error %d at %d\n", error, i);
+      }
+   }
+   #endif
+
+   for (j=1; j < argc; ++j) {
+      unsigned int successes=0, attempts = 0;
+      unsigned int num_samples = stb_vorbis_decode_filename(argv[j], &num_chan, &samprate, &output);
+
+      break;
+
+      if (num_samples == 0xffffffff) {
+         fprintf(stderr, "Error: couldn't open file or not vorbis file: %s\n", argv[j]);
+         goto fail;
+      }
+
+      if (num_chan != 2) {
+         fprintf(stderr, "vorbseek testing only works with files with 2 channels, %s has %d\n", argv[j], num_chan);
+         goto fail;
+      }
+
+      for (test=0; test < 5; ++test) {
+         int error;
+         stb_vorbis *v = stb_vorbis_open_filename(argv[j], &error, NULL);
+         if (v == NULL) {
+            fprintf(stderr, "Couldn't re-open %s for test #%d\n", argv[j], test);
+            goto fail;
+         }
+         for (phase=0; phase < 3; ++phase) {
+            unsigned int base = phase == 0 ? 0 : phase == 1 ? num_samples - test_count[test]*test_spacing[test] : num_samples/3;
+            for (i=0; i < test_count[test]; ++i) {
+               unsigned int pos = base + i*test_spacing[test];
+               if (pos > num_samples) // this also catches underflows
+                  continue;
+               successes += try_seeking(v, pos, output, num_samples);
+               attempts += 1;
+            }
+         }
+         stb_vorbis_close(v);
+      }
+      printf("%d of %d seeks failed in %s (%d samples)\n", attempts-successes, attempts, argv[j], num_samples);
+      free(output);
+   }
+   return 0;
+  fail:
+   return 1;
+}

+ 96 - 0
tests/vorbseek/vorbseek.dsp

@@ -0,0 +1,96 @@
+# Microsoft Developer Studio Project File - Name="vorbseek" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=vorbseek - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "vorbseek.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "vorbseek.mak" CFG="vorbseek - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "vorbseek - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "vorbseek - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "vorbseek - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /Zd /O2 /I "..\.." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+
+!ELSEIF  "$(CFG)" == "vorbseek - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ  /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\.." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ  /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "vorbseek - Win32 Release"
+# Name "vorbseek - Win32 Debug"
+# Begin Source File
+
+SOURCE=..\..\stb_vorbis.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\vorbseek.c
+# End Source File
+# End Target
+# End Project

+ 36 - 11
tools/README.footer.md

@@ -4,15 +4,40 @@ FAQ
 
 #### What's the license?
 
-These libraries are in the public domain (or the equivalent where that is not
-possible). You can do anything you want with them. You have no legal obligation
+These libraries are in the public domain. You can do anything you
+want with them. You have no legal obligation
 to do anything else, although I appreciate attribution.
 
-#### If I wrap an stb library in a new library, does the new library have to be public domain?
+They are also licensed under the MIT open source license, if you have lawyers
+who are unhappy with public domain. Every source file includes an explicit
+dual-license for you to choose from.
 
-No.
+#### <a name="other_libs"></a> Are there other single-file public-domain/open source libraries with minimal dependencies out there?
 
-#### A lot of these libraries seem redundant to existing open source libraries. Are they better somehow?
+[Yes.](https://github.com/nothings/single_file_libs)
+
+#### If I wrap an stb library in a new library, does the new library have to be public domain/MIT?
+
+No, because it's public domain you can freely relicense it to whatever license your new
+library wants to be.
+
+#### What's the deal with SSE support in GCC-based compilers?
+
+stb_image will either use SSE2 (if you compile with -msse2) or
+will not use any SIMD at all, rather than trying to detect the
+processor at runtime and handle it correctly. As I understand it,
+the approved path in GCC for runtime-detection require
+you to use multiple source files, one for each CPU configuration.
+Because stb_image is a header-file library that compiles in only
+one source file, there's no approved way to build both an
+SSE-enabled and a non-SSE-enabled variation.
+
+While we've tried to work around it, we've had multiple issues over
+the years due to specific versions of gcc breaking what we're doing,
+so we've given up on it. See https://github.com/nothings/stb/issues/280
+and https://github.com/nothings/stb/issues/410 for examples.
+
+#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow?
 
 Generally they're only better in that they're easier to integrate,
 easier to use, and easier to release (single file; good API; no
@@ -20,6 +45,10 @@ attribution requirement). They may be less featureful, slower,
 and/or use more memory. If you're already using an equivalent
 library, there's probably no good reason to switch.
 
+#### Can I link directly to the table of stb libraries?
+
+You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list.
+
 #### Why do you list "lines of code"? It's a terrible metric.
 
 Just to give you some idea of the internal complexity of the library,
@@ -57,10 +86,10 @@ remember to attach *two* files, etc.
 #### Why "stb"? Is this something to do with Set-Top Boxes?
 
 No, they are just the initials for my name, Sean T. Barrett.
-This was not chosen out of egomania, but as a semi-robust
+This was not chosen out of egomania, but as a moderately sane
 way of namespacing the filenames and source function names.
 
-#### Will you add more image types to stb_image.c?
+#### Will you add more image types to stb_image.h?
 
 If people submit them, I generally add them, but the goal of stb_image
 is less for applications like image viewer apps (which need to support
@@ -68,10 +97,6 @@ every type of image under the sun) and more for things like games which
 can choose what images to use, so I may decline to add them if they're
 too rare or if the size of implementation vs. apparent benefit is too low.
 
-#### Are there other single-file public-domain libraries out there?
-
-Yes. I'll put a list here when people remind me what they are.
-
 #### Do you have any advice on how to create my own single-file library?
 
 Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt

+ 5 - 1
tools/README.header.md

@@ -1,7 +1,11 @@
 stb
 ===
 
-single-file public domain libraries for C/C++
+single-file public domain (or MIT licensed) libraries for C/C++ <a name="stb_libs"></a>
+
+Most libraries by stb, except: stb_dxt by Fabian "ryg" Giesen, stb_image_resize
+by Jorge L. "VinoBS" Rodriguez, and stb_sprintf by Jeff Roberts.
+
 
 library    | lastest version | category | LoC | description
 --------------------- | ---- | -------- | --- | --------------------------------

+ 3 - 1
tools/README.list

@@ -4,8 +4,9 @@ stb_truetype.h              | graphics         | parse, decode, and rasterize ch
 stb_image_write.h           | graphics         | image writing to disk: PNG, TGA, BMP
 stb_image_resize.h          | graphics         | resize images larger/smaller with good quality
 stb_rect_pack.h             | graphics         | simple 2D rectangle packer with decent quality
+stb_sprintf.h               | utility          | fast sprintf, snprintf for C/C++
 stretchy_buffer.h           | utility          | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
-stb_textedit.h              | UI               | guts of a text editor for games etc implementing them from scratch
+stb_textedit.h              | user interface   | guts of a text editor for games etc implementing them from scratch
 stb_voxel_render.h          | 3D graphics      | Minecraft-esque voxel rendering "engine" with many more features
 stb_dxt.h                   | 3D graphics      | Fabian "ryg" Giesen's real-time DXT compressor
 stb_perlin.h                | 3D graphics      | revised Perlin noise (3D input, 1D output)
@@ -14,5 +15,6 @@ stb_tilemap_editor.h        | game dev         | embeddable tilemap editor
 stb_herringbone_wang_tile.h | game dev         | herringbone Wang tile map generator
 stb_c_lexer.h               | parsing          | simplify writing parsers for C-like languages
 stb_divide.h                | math             | more useful 32-bit modulus e.g. "euclidean divide"
+stb_connected_components.h  | misc             | incrementally compute reachability on grids
 stb.h                       | misc             | helper functions for C, mostly redundant in C++; basically author's personal stuff
 stb_leakcheck.h             | misc             | quick-and-dirty malloc/free leak-checking

+ 5 - 2
tools/make_readme.c

@@ -30,15 +30,18 @@ int main(int argc, char  **argv)
       if (*s1 == 'v') ++s1;
       s3 = tokens[0];
       stb_trimwhite(s3);
+      fprintf(f, "**[");
       if (strlen(s3) < 21) {
-         fprintf(f, "**%s** | %s", tokens[0], s1);
+         fprintf(f, "%s", tokens[0]);
       } else {
          char buffer[256];
          strncpy(buffer, s3, 18);
          buffer[18] = 0;   
          strcat(buffer, "...");
-         fprintf(f, "**%s** | %s", buffer, s1);
+         fprintf(f, "%s", buffer);
       }
+      fprintf(f, "](%s)**", tokens[0]);
+      fprintf(f, " | %s", s1);
       s1 = stb_trimwhite(tokens[1]);           // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace
       s2 = stb_dupreplace(s1, " ", "&nbsp;");  // stb_dupreplace -- search & replace string and malloc result
       fprintf(f, " | %s", s2);

+ 749 - 0
tools/unicode.c

@@ -0,0 +1,749 @@
+#define STB_DEFINE
+#include "../stb.h"
+
+// create unicode mappings
+//
+// Two kinds of mappings:
+//     map to a number
+//     map to a bit
+//
+// For mapping to a number, we use the following strategy:
+//
+//   User supplies:
+//     1. a table of numbers (for now we use uint16, so full Unicode table is 4MB)
+//     2. a "don't care" value
+//     3. define a 'fallback' value (typically 0)
+//     4. define a fast-path range (typically 0..255 or 0..1023) [@TODO: automate detecting this]
+//
+//   Code:
+//     1. Determine range of *end* of unicode codepoints (U+10FFFF and down) which
+//        all have the same value (or don't care). If large enough, emit this as a
+//        special case in the code.
+//     2. Repeat above, limited to at most U+FFFF.
+//     3. Cluster the data into intervals of 8,16,32,64,128,256 numeric values.
+//        3a. If all the values in an interval are fallback/dont-care, no further processing
+//        3b. Find the "trimmed range" outside which all the values are the fallback or don't care
+//        3c. Find the "special trimmed range" outside which all the values are some constant or don't care
+//     4. Pack the clusters into continuous memory, and find previous instances of
+//        the cluster. Repeat for trimmed & special-trimmed. In the first case, find
+//        previous instances of the cluster (allow don't-care to match in either
+//        direction), both aligned and mis-aligned; in the latter, starting where
+//        things start or mis-aligned. Build an index table specifiying the
+//        location of each cluster (and its length). Allow an extra indirection here;
+//        the full-sized index can index a smaller table which has the actual offset
+//        (and lengths).
+//     5. Associate with each packed continuous memory above the amount of memory
+//        required to store the data w/ smallest datatype (of uint8, uint16, uint32).
+//        Discard the continuous memory. Recurse on each index table, but avoid the
+//        smaller packing.
+//
+// For mapping to a bit, we pack the results for 8 characters into a byte, and then apply
+// the above strategy. Note that there may be more optimal approaches with e.g. packing
+// 8 different bits into a single structure, though, which we should explore eventually.
+
+
+// currently we limit *indices* to being 2^16, and we pack them as
+//      index + end_trim*2^16 + start_trim*2^24; specials have to go in a separate table
+typedef uint32 uval;
+#define UVAL_DONT_CARE_DEFAULT 0xffffffff
+
+typedef struct
+{
+   uval *input;
+   uint32 dont_care;
+   uint32 fallback;
+   int  fastpath;
+   int  length;
+   int  depth;
+   int  has_sign;
+   int  splittable;
+   int  replace_fallback_with_codepoint;
+   size_t input_size;
+   size_t inherited_storage;
+} table;
+
+typedef struct
+{
+   int   split_log2;
+   table result; // index into not-returned table
+   int   storage;
+} output;
+
+typedef struct
+{
+   table t;
+   char **output_name;
+} info;
+
+typedef struct
+{
+   size_t path;
+   size_t size;
+} result;
+
+typedef struct
+{
+   uint8 trim_end;
+   uint8 trim_start;
+   uint8 special;
+   uint8 aligned;
+   uint8 indirect;
+
+   uint16 overhead; // add some forced overhead for each mode to avoid getting complex encoding when it doesn't save much
+
+} mode_info;
+
+mode_info modes[] =
+{
+   {   0,0,0,0,0,    32, },
+   {   0,0,0,0,1,   100, },
+   {   0,0,0,1,0,    32, },
+   {   0,0,0,1,1,   100, },
+   {   0,0,1,0,1,   100, },
+   {   0,0,1,1,0,    32, },
+   {   0,0,1,1,1,   200, },
+   {   1,0,0,0,0,   100, },
+   {   1,0,0,0,1,   120, },
+   {   1,1,0,0,0,   100, },
+   {   1,1,0,0,1,   130, },
+   {   1,0,1,0,0,   130, },
+   {   1,0,1,0,1,   180, },
+   {   1,1,1,0,0,   180, },
+   {   1,1,1,0,1,   200, },
+};
+
+#define MODECOUNT  (sizeof(modes)/sizeof(modes[0]))
+#define CLUSTERSIZECOUNT   6    // 8,16, 32,64,  128,256
+
+size_t size_for_max_number(uint32 number)
+{
+   if (number == 0) return 0;
+   if (number < 256) return 1;
+   if (number < 256*256) return 2;
+   if (number < 256*256*256) return 3;
+   return 4;
+}
+
+size_t size_for_max_number_aligned(uint32 number)
+{
+   size_t n = size_for_max_number(number);
+   return n == 3 ? 4 : n;
+}
+
+uval get_data(uval *data, int offset, uval *end)
+{
+   if (data + offset >= end)
+      return 0;
+   else
+      return data[offset];
+}
+
+int safe_len(uval *data, int len, uval *end)
+{
+   if (len > end - data)
+      return end - data;
+   return len;
+}
+
+uval tempdata[256];
+int dirty=0;
+
+size_t find_packed(uval **packed, uval *data, int len, int aligned, int fastpath, uval *end, int offset, int replace)
+{
+   int packlen = stb_arr_len(*packed);
+   int i,p;
+
+   if (data+len > end || replace) {
+      int safelen = safe_len(data, len, end);
+      memset(tempdata, 0, dirty*sizeof(tempdata[0]));
+      memcpy(tempdata, data, safelen * sizeof(data[0]));
+      data = tempdata;
+      dirty = len;
+   }
+   if (replace) {
+      int i;
+      int safelen = safe_len(data, len, end);
+      for (i=0; i < safelen; ++i)
+         if (data[i] == 0)
+            data[i] = offset+i;
+   }
+
+   if (len <= 0)
+      return 0;
+   if (!fastpath) {
+      if (aligned) {
+         for (i=0; i < packlen; i += len)
+            if ((*packed)[i] == data[0] && 0==memcmp(&(*packed)[i], data, len * sizeof(uval)))
+               return i / len;
+      } else {
+         for (i=0; i < packlen-len+1; i +=  1 )
+            if ((*packed)[i] == data[0] && 0==memcmp(&(*packed)[i], data, len * sizeof(uval)))
+               return i;
+      }
+   }
+   p = stb_arr_len(*packed);
+   for (i=0; i < len; ++i)
+      stb_arr_push(*packed, data[i]);
+   return p;
+}
+
+void output_table(char *name1, char *name2, uval *data, int length, int sign, char **names)
+{
+   char temp[20];
+   uval maxv = 0;
+   int bytes, numlen, at_newline;
+   int linelen = 79; // @TODO: make table more readable by choosing a length that's a multiple?
+   int i,pos, do_split=0;
+   for (i=0; i < length; ++i)
+      if (sign)
+         maxv = stb_max(maxv, (uval)abs((int)data[i]));
+      else
+         maxv = stb_max(maxv, data[i]);
+   bytes = size_for_max_number_aligned(maxv);
+   sprintf(temp, "%d", maxv);
+   numlen=strlen(temp);
+   if (sign)
+      ++numlen;
+   
+   if (bytes == 0)
+      return;
+
+   printf("uint%d %s%s[%d] = {\n", bytes*8, name1, name2, length);
+   at_newline = 1;
+   for (i=0; i < length; ++i) {
+      if (pos + numlen + 2 > linelen) {
+         printf("\n");
+         at_newline = 1;
+         pos = 0;
+      }
+      if (at_newline) {
+         printf("  ");
+         pos = 2;
+         at_newline = 0;
+      } else {
+         printf(" ");
+         ++pos;
+      }
+      printf("%*d,", numlen, data[i]);
+      pos += numlen+1;
+   }
+   if (!at_newline) printf("\n");
+   printf("};\n");
+}
+
+void output_table_with_trims(char *name1, char *name2, uval *data, int length)
+{
+   uval maxt=0, maxp=0;
+   int i,d,s,e, count;
+   // split the table into two pieces
+   uval *trims = NULL;
+
+   if (length == 0)
+      return;
+
+   for (i=0; i < stb_arr_len(data); ++i) {
+      stb_arr_push(trims, data[i] >> 16);
+      data[i] &= 0xffff;
+      maxt = stb_max(maxt, trims[i]);
+      maxp = stb_max(maxp, data[i]);
+   }
+
+   d=s=e=1;
+   if (maxt >= 256) {
+      // need to output start & end values
+      if (maxp >= 256) {
+         // can pack into a single table
+         printf("struct { uint16 val; uint8 start, end; } %s%s[%d] = {\n", name1, name2, length);
+      } else {
+         output_table(name1, name2, data, length, 0, 0);
+         d=0;
+         printf("struct { uint8 start, end; } %s%s_trim[%d] = {\n", name1, name2, length);
+      }
+   } else if (maxt > 0) {
+      if (maxp >= 256) {
+         output_table(name1, name2, data, length, 0, 0);
+         output_table(name1, stb_sprintf("%s_end", name2), trims, length, 0, 0);
+         return;
+      } else {
+         printf("struct { uint8 val, end; } %s%s[%d] = {\n", name1, name2, length);
+         s=0;
+      }
+   } else {
+      output_table(name1, name2, data, length, 0, 0);
+      return;
+   }
+   // d or s can be zero (but not both), e is always present and last
+   count = d + s + e;
+   assert(count >= 2 && count <= 3);
+
+   {
+      char temp[60];
+      uval maxv = 0;
+      int numlen, at_newline, len;
+      int linelen = 79; // @TODO: make table more readable by choosing a length that's a multiple?
+      int i,pos, do_split=0;
+      numlen = 0;
+      for (i=0; i < length; ++i) {
+         if (count == 2)
+            sprintf(temp, "{%d,%d}", d ? data[i] : (trims[i]>>8), trims[i]&255);
+         else
+            sprintf(temp, "{%d,%d,%d}", data[i], trims[i]>>8, trims[i]&255);
+         len = strlen(temp);
+         numlen = stb_max(len, numlen);
+      }
+   
+      at_newline = 1;
+      for (i=0; i < length; ++i) {
+         if (pos + numlen + 2 > linelen) {
+            printf("\n");
+            at_newline = 1;
+            pos = 0;
+         }
+         if (at_newline) {
+            printf("  ");
+            pos = 2;
+            at_newline = 0;
+         } else {
+            printf(" ");
+            ++pos;
+         }
+         if (count == 2)
+            sprintf(temp, "{%d,%d}", d ? data[i] : (trims[i]>>8), trims[i]&255);
+         else
+            sprintf(temp, "{%d,%d,%d}", data[i], trims[i]>>8, trims[i]&255);
+         printf("%*s,", numlen, temp);
+         pos += numlen+1;
+      }
+      if (!at_newline) printf("\n");
+      printf("};\n");
+   }
+}
+
+int weight=1;
+
+table pack_for_mode(table *t, int mode, char *table_name)
+{
+   size_t extra_size;
+   int i;
+   uval maxv;
+   mode_info mi = modes[mode % MODECOUNT];
+   int size = 8 << (mode / MODECOUNT);
+   table newtab;
+   uval *packed = NULL;
+   uval *index = NULL;
+   uval *indirect = NULL;
+   uval *specials = NULL;
+   newtab.dont_care = UVAL_DONT_CARE_DEFAULT;
+   if (table_name)
+      printf("// clusters of %d\n", size);
+   for (i=0; i < t->length; i += size) {
+      uval newval;
+      int fastpath = (i < t->fastpath);
+      if (mi.special) {
+         int end_trim = size-1;
+         int start_trim = 0;
+         uval special;
+         // @TODO: pick special from start or end instead of only end depending on which is longer
+         for(;;) {
+            special = t->input[i + end_trim];
+            if (special != t->dont_care || end_trim == 0)
+               break;
+            --end_trim;
+         }
+         // at this point, special==inp[end_trim], and end_trim >= 0
+         if (special == t->dont_care && !fastpath) {
+            // entire block is don't care, so OUTPUT don't care
+            stb_arr_push(index, newtab.dont_care);
+            continue;
+         } else {
+            uval pos, trim;
+            if (mi.trim_end && !fastpath) {
+               while (end_trim >= 0) {
+                  if (t->input[i + end_trim] == special || t->input[i + end_trim] == t->dont_care)
+                     --end_trim;
+                  else
+                     break;
+               }
+            }
+
+            if (mi.trim_start && !fastpath) {
+               while (start_trim < end_trim) {
+                  if (t->input[i + start_trim] == special || t->input[i + start_trim] == t->dont_care)
+                     ++start_trim;
+                  else
+                     break;
+               }
+            }
+
+            // end_trim points to the last character we have to output
+
+            // find the first match, or add it
+            pos = find_packed(&packed, &t->input[i+start_trim], end_trim-start_trim+1, mi.aligned, fastpath, &t->input[t->length], i+start_trim, t->replace_fallback_with_codepoint);
+
+            // encode as a uval
+            if (!mi.trim_end) {
+               if (end_trim == 0)
+                  pos = special;
+               else
+                  pos = pos | 0x80000000;
+            } else {
+               assert(end_trim < size && end_trim >= -1);
+               if (!fastpath) assert(end_trim < size-1); // special always matches last one
+               assert(end_trim < size && end_trim+1 >= 0);
+               if (!fastpath) assert(end_trim+1 < size);
+
+               if (mi.trim_start)
+                  trim = start_trim*256 + (end_trim+1);
+               else
+                  trim = end_trim+1;
+
+               assert(pos < 65536); // @TODO: if this triggers, just bail on this search path
+               pos = pos + (trim << 16);
+            }
+
+            newval = pos;
+
+            stb_arr_push(specials, special);
+         }
+      } else if (mi.trim_end) {
+         int end_trim = size-1;
+         int start_trim = 0;
+         uval pos, trim;
+
+         while (end_trim >= 0 && !fastpath)
+            if (t->input[i + end_trim] == t->fallback || t->input[i + end_trim] == t->dont_care)
+               --end_trim;
+            else
+               break;
+
+         if (mi.trim_start && !fastpath) {
+            while (start_trim < end_trim) {
+               if (t->input[i + start_trim] == t->fallback || t->input[i + start_trim] == t->dont_care)
+                  ++start_trim;
+               else
+                  break;
+            }
+         }
+
+         // end_trim points to the last character we have to output, and can be -1
+         ++end_trim; // make exclusive at end
+
+         if (end_trim == 0 && size == 256)
+            start_trim = end_trim = 1;  // we can't make encode a length from 0..256 in 8 bits, so restrict end_trim to 1..256
+
+         // find the first match, or add it
+         pos = find_packed(&packed, &t->input[i+start_trim], end_trim - start_trim, mi.aligned, fastpath, &t->input[t->length], i+start_trim, t->replace_fallback_with_codepoint);
+
+         assert(end_trim <= size && end_trim >= 0);
+         if (size == 256)
+            assert(end_trim-1 < 256 && end_trim-1 >= 0);
+         else
+            assert(end_trim < 256 && end_trim >= 0);
+         if (size == 256)
+            --end_trim;
+
+         if (mi.trim_start)
+            trim = start_trim*256 + end_trim;
+         else
+            trim = end_trim;
+
+         assert(pos < 65536); // @TODO: if this triggers, just bail on this search path
+         pos = pos + (trim << 16);
+
+         newval = pos;
+      } else {
+         newval = find_packed(&packed, &t->input[i], size, mi.aligned, fastpath, &t->input[t->length], i, t->replace_fallback_with_codepoint);
+      }
+
+      if (mi.indirect) {
+         int j;
+         for (j=0; j < stb_arr_len(indirect); ++j)
+            if (indirect[j] == newval)
+               break;
+         if (j == stb_arr_len(indirect))
+            stb_arr_push(indirect, newval);
+         stb_arr_push(index, j);
+      } else {
+         stb_arr_push(index, newval);
+      }
+   }
+
+   // total up the new size for everything but the index table
+   extra_size = mi.overhead * weight; // not the actual overhead cost; a penalty to avoid excessive complexity
+   extra_size += 150; // per indirection
+   if (table_name)
+      extra_size = 0;
+   
+   if (t->has_sign) {
+      // 'packed' contains two values, which should be packed positive & negative for size
+      uval maxv2;
+      for (i=0; i < stb_arr_len(packed); ++i)
+         if (packed[i] & 0x80000000)
+            maxv2 = stb_max(maxv2, packed[i]);
+         else
+            maxv  = stb_max(maxv, packed[i]);
+      maxv = stb_max(maxv, maxv2) << 1;
+   } else {
+      maxv = 0;
+      for (i=0; i < stb_arr_len(packed); ++i)
+         if (packed[i] > maxv && packed[i] != t->dont_care)
+            maxv = packed[i];
+   }
+   extra_size += stb_arr_len(packed) * (t->splittable ? size_for_max_number(maxv) : size_for_max_number_aligned(maxv));
+   if (table_name) {
+      if (t->splittable)
+         output_table_with_trims(table_name, "", packed, stb_arr_len(packed));
+      else
+         output_table(table_name, "", packed, stb_arr_len(packed), t->has_sign, NULL);
+   }
+
+   maxv = 0;
+   for (i=0; i < stb_arr_len(specials); ++i)
+      if (specials[i] > maxv)
+         maxv = specials[i];
+   extra_size += stb_arr_len(specials) * size_for_max_number_aligned(maxv);
+   if (table_name)
+      output_table(table_name, "_default", specials, stb_arr_len(specials), 0, NULL);
+
+   maxv = 0;
+   for (i=0; i < stb_arr_len(indirect); ++i)
+      if (indirect[i] > maxv)
+         maxv = indirect[i];
+   extra_size += stb_arr_len(indirect) * size_for_max_number(maxv);
+
+   if (table_name && stb_arr_len(indirect)) {
+      if (mi.trim_end)
+         output_table_with_trims(table_name, "_index", indirect, stb_arr_len(indirect));
+      else {
+         assert(0); // this case should only trigger in very extreme circumstances
+         output_table(table_name, "_index", indirect, stb_arr_len(indirect), 0, NULL);
+      }
+      mi.trim_end = mi.special = 0;
+   }
+
+   if (table_name)
+      printf("// above tables should be %d bytes\n", extra_size);
+
+   maxv = 0;
+   for (i=0; i < stb_arr_len(index); ++i)
+      if (index[i] > maxv && index[i] != t->dont_care)
+         maxv = index[i];
+   newtab.splittable = mi.trim_end;
+   newtab.input_size = newtab.splittable ? size_for_max_number(maxv) : size_for_max_number_aligned(maxv);
+   newtab.input = index;
+   newtab.length = stb_arr_len(index);
+   newtab.inherited_storage = t->inherited_storage + extra_size;
+   newtab.fastpath = 0;
+   newtab.depth = t->depth+1;
+   stb_arr_free(indirect);
+   stb_arr_free(packed);
+   stb_arr_free(specials);
+
+   return newtab;
+}
+
+result pack_table(table *t, size_t path, int min_storage)
+{
+   int i;
+   result best;
+   best.size = t->inherited_storage + t->input_size * t->length;
+   best.path = path;
+
+   if ((int) t->inherited_storage > min_storage) {
+      best.size = stb_max(best.size, t->inherited_storage);
+      return best;
+   }
+
+   if (t->length <= 256 || t->depth >= 4) {
+      //printf("%08x: %7d\n", best.path, best.size);
+      return best;
+   }
+
+   path <<= 7;
+   for (i=0; i < MODECOUNT * CLUSTERSIZECOUNT; ++i) {
+      table newtab;
+      result r;
+      newtab = pack_for_mode(t, i, 0);
+      r = pack_table(&newtab, path+i+1, min_storage);
+      if (r.size < best.size)
+         best = r;
+      stb_arr_free(newtab.input);
+      //printf("Size: %6d + %6d\n", newtab.inherited_storage, newtab.input_size * newtab.length);
+   }
+   return best;
+}
+
+int pack_table_by_modes(table *t, int *modes)
+{
+   table s = *t;
+   while (*modes > -1) {
+      table newtab;
+      newtab = pack_for_mode(&s, *modes, 0);
+      if (s.input != t->input)
+         stb_arr_free(s.input);
+      s = newtab;
+      ++modes;
+   }
+   return s.inherited_storage + s.input_size * s.length;
+}
+
+int strip_table(table *t, int exceptions)
+{
+   uval terminal_value;
+   int p = t->length-1;
+   while (t->input[p] == t->dont_care)
+      --p;
+   terminal_value = t->input[p];
+
+   while (p >= 0x10000) {
+      if (t->input[p] != terminal_value && t->input[p] != t->dont_care) {
+         if (exceptions)
+            --exceptions;
+         else
+            break;
+      }
+      --p;
+   }
+   return p+1; // p is a character we must output
+}
+
+void optimize_table(table *t, char *table_name)
+{
+   int modelist[3] = { 85, -1 };
+   int modes[8];
+   int num_modes = 0;
+   int decent_size;
+   result r;
+   size_t path;
+   table s;
+
+   // strip tail end of table
+   int orig_length = t->length;
+   int threshhold = 0xffff;
+   int p = strip_table(t, 2);
+   int len_saved = t->length - p;
+   if (len_saved >= threshhold) {
+      t->length = p;
+      while (p > 0x10000) {
+         p = strip_table(t, 0);
+         len_saved = t->length - p;
+         if (len_saved < 0x10000)
+            break;
+         len_saved = orig_length - p;
+         if (len_saved < threshhold)
+            break;
+         threshhold *= 2;
+      }
+   }
+
+   t->depth = 1;
+
+
+   // find size of table if we use path 86
+   decent_size = pack_table_by_modes(t, modelist);
+
+
+   #if 1
+   // find best packing of remainder of table by exploring tree of packings
+   r = pack_table(t, 0, decent_size);
+   // use the computed 'path' to evaluate and output tree
+   path = r.path;
+   #else
+   path = 86;//90;//132097;
+   #endif
+
+   while (path) {
+      modes[num_modes++] = (path & 127) - 1;
+      path >>= 7;
+   }
+
+   printf("// modes: %d\n", r.path);
+   s = *t;
+   while (num_modes > 0) {
+      char name[256];
+      sprintf(name, "%s_%d", table_name, num_modes+1);
+      --num_modes;
+      s = pack_for_mode(&s, modes[num_modes], name);
+   }
+   // output the final table as-is
+   if (s.splittable)
+      output_table_with_trims(table_name, "_1", s.input, s.length);
+   else
+      output_table(table_name, "_1", s.input, s.length, 0, NULL);
+}
+
+uval unicode_table[0x110000];
+
+typedef struct
+{
+   uval lo,hi;
+} char_range;
+
+char_range get_range(char *str)
+{
+   char_range cr;
+   char *p;
+   cr.lo = strtol(str, &p, 16);
+   p = stb_skipwhite(p);
+   if (*p == '.')
+      cr.hi = strtol(p+2, NULL, 16);
+   else
+      cr.hi = cr.lo;
+   return cr;
+}
+
+char *skip_semi(char *s, int count)
+{
+   while (count) {
+      s = strchr(s, ';');
+      assert(s != NULL);
+      ++s;
+      --count;
+   }
+   return s;
+}
+
+int main(int argc, char **argv)
+{
+   table t;
+   uval maxv=0;
+   int i,n=0;
+   char **s = stb_stringfile("../../data/UnicodeData.txt", &n);
+   assert(s);
+   for (i=0; i < n; ++i) {
+      if (s[i][0] == '#' || s[i][0] == '\n' || s[i][0] == 0)
+         ;
+      else {
+         char_range cr = get_range(s[i]);
+         char *t = skip_semi(s[i], 13);
+         uval j, v;
+         if (*t == ';' || *t == '\n' || *t == 0)
+            v = 0;
+         else {
+            v = strtol(t, NULL, 16);
+            if (v < 65536) {
+               maxv = stb_max(v, maxv);
+               for (j=cr.lo; j <= cr.hi; ++j) {
+                  unicode_table[j] = v;
+                  //printf("%06x => %06x\n", j, v);
+               }
+            }
+         }
+      }
+   }
+
+   t.depth = 0;
+   t.dont_care = UVAL_DONT_CARE_DEFAULT;
+   t.fallback = 0;
+   t.fastpath = 256;
+   t.inherited_storage = 0;
+   t.has_sign = 0;
+   t.splittable = 0;
+   t.input = unicode_table;
+   t.input_size = size_for_max_number(maxv);
+   t.length = 0x110000;
+   t.replace_fallback_with_codepoint = 1;
+
+   optimize_table(&t, "stbu_upppercase");
+   return 0;
+}

+ 88 - 0
tools/unicode/unicode.dsp

@@ -0,0 +1,88 @@
+# Microsoft Developer Studio Project File - Name="unicode" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=unicode - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "unicode.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "unicode.mak" CFG="unicode - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "unicode - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "unicode - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "unicode - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF  "$(CFG)" == "unicode - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ  /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ  /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "unicode - Win32 Release"
+# Name "unicode - Win32 Debug"
+# Begin Source File
+
+SOURCE=..\unicode.c
+# End Source File
+# End Target
+# End Project

部分文件因文件數量過多而無法顯示