summaryrefslogtreecommitdiff
path: root/thirdparty/stb/tests/oversample
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /thirdparty/stb/tests/oversample
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'thirdparty/stb/tests/oversample')
-rw-r--r--thirdparty/stb/tests/oversample/README.md94
-rw-r--r--thirdparty/stb/tests/oversample/main.c332
-rw-r--r--thirdparty/stb/tests/oversample/oversample.dsp97
-rw-r--r--thirdparty/stb/tests/oversample/oversample.dsw29
-rw-r--r--thirdparty/stb/tests/oversample/oversample.exebin0 -> 54272 bytes
-rw-r--r--thirdparty/stb/tests/oversample/stb_wingraph.h829
6 files changed, 1381 insertions, 0 deletions
diff --git a/thirdparty/stb/tests/oversample/README.md b/thirdparty/stb/tests/oversample/README.md
new file mode 100644
index 0000000..cdfdfff
--- /dev/null
+++ b/thirdparty/stb/tests/oversample/README.md
@@ -0,0 +1,94 @@
+# Font character oversampling for rendering from atlas textures
+
+TL,DR: Run oversample.exe on a windows machine to see the
+benefits of oversampling. It will try to use arial.ttf from the
+Windows font directory unless you type the name of a .ttf file as
+a command-line argument.
+
+## Benefits of oversampling
+
+Oversampling is a mechanism for improving subpixel rendering of characters.
+
+Improving subpixel has a few benefits:
+
+* With horizontal-oversampling, text can remain sharper while still being sub-pixel positioned for better kerning
+* Horizontally-oversampled text significantly reduces aliasing when text animates horizontally
+* Vertically-oversampled text significantly reduces aliasing when text animates vertically
+* Text oversampled in both directions significantly reduces aliasing when text rotates
+
+## What text oversampling is
+
+A common strategy for rendering text is to cache character bitmaps
+and reuse them. For hinted characters, every instance of a given
+character is always identical, so this works fine. However, stb_truetype
+doesn't do hinting.
+
+For anti-aliased characters, you can actually position the characters
+with subpixel precision, and get different bitmaps based on that positioning
+if you re-render the vector data.
+
+However, if you simply cache a single version of the bitmap and
+draw it at different subpixel positions with a GPU, you will get
+either the exact same result (if you use point-sampling on the
+texture) or linear filtering. Linear filtering will cause a sub-pixel
+positioned bitmap to blur further, causing a visible de-sharpening
+of the character. (And, since the character wasn't hinted, it was
+already blurrier than a hinted one would be, and now it gets even
+more blurry.)
+
+You can avoid this by caching multiple variants of a character which
+were rendered independently from the vector data. For example, you
+might cache 3 versions of a char, at 0, 1/3, and 2/3rds of a pixel
+horizontal offset, and always require characters to fall on integer
+positions vertically.
+
+When creating a texture atlas for use on GPUs, which support bilinear
+filtering, there is a better approach than caching several independent
+positions, which is to allow lerping between the versions to allow
+finer subpixel positioning. You can achieve these by interleaving
+each of the cached bitmaps, but this turns out to be mathematically
+equivalent to a simpler operation: oversampling and prefiltering the
+characters.
+
+So, setting oversampling of 2x2 in stb_truetype is equivalent to caching
+each character in 4 different variations, 1 for each subpixel position
+in a 2x2 set.
+
+An advantage of this formulation is that no changes are required to
+the rendering code; the exact same quad-rendering code works, it just
+uses different texture coordinates. (Note this does potentially increase
+texture bandwidth for text rendering since we end up minifying the texture
+without using mipmapping, but you probably are not going to be fill-bound
+by your text rendering.)
+
+## What about gamma?
+
+Gamma-correction for fonts just doesn't work. This doesn't seem to make
+much sense -- it's physically correct, it simulates what we'd see if you
+shrunk a font down really far, right?
+
+But you can play with it in the oversample.exe app. If you turn it on,
+white-on-black fonts become too thick (i.e. they become too bright), and
+black-on-white fonts become too thin (i.e. they are insufficiently dark). There is
+no way to adjust the font's inherent thickness (i.e. by switching to
+bold) to fix this for both; making the font thicker will make white
+text worse, and making the font thinner will make black text worse.
+Obviously you could use different fonts for light and dark cases, but
+this doesn't seem like a very good way for fonts to work.
+
+Multiple people who have experimented with this independently (me,
+Fabian Giesen,and Maxim Shemanarev of Anti-Grain Geometry) have all
+concluded that correct gamma-correction does not produce the best
+results for fonts. Font rendering just generally looks better without
+gamma correction (or possibly with some arbitrary power stuck in
+there, but it's not really correcting for gamma at that point). Maybe
+this is in part a product of how we're used to fonts being on screens
+which has changed how we expect them to look (e.g. perhaps hinting
+oversharpens them and prevents the real-world thinning you'd see in
+a black-on-white text).
+
+(AGG link on text rendering, including mention of gamma:
+ http://www.antigrain.com/research/font_rasterization/ )
+
+Nevertheless, even if you turn on gamma-correction, you will find that
+oversampling still helps in many cases for small fonts.
diff --git a/thirdparty/stb/tests/oversample/main.c b/thirdparty/stb/tests/oversample/main.c
new file mode 100644
index 0000000..bc6bd0f
--- /dev/null
+++ b/thirdparty/stb/tests/oversample/main.c
@@ -0,0 +1,332 @@
+#pragma warning(disable:4244; disable:4305; disable:4018)
+#include <assert.h>
+#include <ctype.h>
+
+#define STB_WINMAIN
+#include "stb_wingraph.h"
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#define STB_RECT_PACK_IMPLEMENTATION
+#include "stb_rect_pack.h"
+#include "stb_truetype.h"
+
+#ifndef WINGDIAPI
+#define CALLBACK __stdcall
+#define WINGDIAPI __declspec(dllimport)
+#define APIENTRY __stdcall
+#endif
+
+#include <gl/gl.h>
+#include <gl/glu.h>
+
+#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9
+
+#define SIZE_X 1024
+#define SIZE_Y 768
+
+stbtt_packedchar chardata[6][128];
+
+int sx=SIZE_X, sy=SIZE_Y;
+
+#define BITMAP_W 512
+#define BITMAP_H 512
+unsigned char temp_bitmap[BITMAP_W][BITMAP_H];
+unsigned char ttf_buffer[1 << 25];
+GLuint font_tex;
+
+float scale[2] = { 24.0f, 14.0f };
+
+int sf[6] = { 0,1,2, 0,1,2 };
+
+void load_fonts(void)
+{
+ stbtt_pack_context pc;
+ int i;
+ FILE *f;
+ char filename[256];
+ char *win = getenv("windir");
+ if (win == NULL) win = getenv("SystemRoot");
+
+ f = fopen(stb_wingraph_commandline, "rb");
+ if (!f) {
+ if (win == NULL)
+ sprintf(filename, "arial.ttf", win);
+ else
+ sprintf(filename, "%s/fonts/arial.ttf", win);
+ f = fopen(filename, "rb");
+ if (!f) exit(0);
+ }
+
+ fread(ttf_buffer, 1, 1<<25, f);
+
+ stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL);
+ for (i=0; i < 2; ++i) {
+ stbtt_PackSetOversampling(&pc, 1, 1);
+ stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+0]+32);
+ stbtt_PackSetOversampling(&pc, 2, 2);
+ stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+1]+32);
+ stbtt_PackSetOversampling(&pc, 3, 1);
+ stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+2]+32);
+ }
+ stbtt_PackEnd(&pc);
+
+ glGenTextures(1, &font_tex);
+ glBindTexture(GL_TEXTURE_2D, font_tex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
+int black_on_white;
+
+void draw_init(void)
+{
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_DEPTH_TEST);
+
+ glViewport(0,0,sx,sy);
+ if (black_on_white)
+ glClearColor(255,255,255,0);
+ else
+ glClearColor(0,0,0,0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluOrtho2D(0,sx,sy,0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
+
+void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1)
+{
+ glTexCoord2f(s0,t0); glVertex2f(x0,y0);
+ glTexCoord2f(s1,t0); glVertex2f(x1,y0);
+ glTexCoord2f(s1,t1); glVertex2f(x1,y1);
+ glTexCoord2f(s0,t1); glVertex2f(x0,y1);
+}
+
+int integer_align;
+
+void print(float x, float y, int font, char *text)
+{
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, font_tex);
+ glBegin(GL_QUADS);
+ while (*text) {
+ stbtt_aligned_quad q;
+ stbtt_GetPackedQuad(chardata[font], BITMAP_W, BITMAP_H, *text++, &x, &y, &q, font ? 0 : integer_align);
+ drawBoxTC(q.x0,q.y0,q.x1,q.y1, q.s0,q.t0,q.s1,q.t1);
+ }
+ glEnd();
+}
+
+int font=3;
+int translating;
+int rotating=0;
+int srgb=0;
+float rotate_t, translate_t;
+int show_tex;
+
+void draw_world(void)
+{
+ int sfont = sf[font];
+ float x = 20;
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ if (black_on_white)
+ glColor3f(0,0,0);
+ else
+ glColor3f(1,1,1);
+
+
+ print(80, 30, sfont, "Controls:");
+ print(100, 60, sfont, "S: toggle font size");
+ print(100, 85, sfont, "O: toggle oversampling");
+ print(100,110, sfont, "T: toggle translation");
+ print(100,135, sfont, "R: toggle rotation");
+ print(100,160, sfont, "P: toggle pixel-snap (only non-oversampled)");
+ print(100,185, sfont, "G: toggle srgb gamma-correction");
+ if (black_on_white)
+ print(100,210, sfont, "B: toggle to white-on-black");
+ else
+ print(100,210, sfont, "B: toggle to black-on-white");
+ print(100,235, sfont, "V: view font texture");
+
+ print(80, 300, sfont, "Current font:");
+
+ if (!show_tex) {
+ if (font < 3)
+ print(100, 350, sfont, "Font height: 24 pixels");
+ else
+ print(100, 350, sfont, "Font height: 14 pixels");
+ }
+
+ if (font%3==1)
+ print(100, 325, sfont, "2x2 oversampled text at 1:1");
+ else if (font%3 == 2)
+ print(100, 325, sfont, "3x1 oversampled text at 1:1");
+ else if (integer_align)
+ print(100, 325, sfont, "1:1 text, one texel = one pixel, snapped to integer coordinates");
+ else
+ print(100, 325, sfont, "1:1 text, one texel = one pixel");
+
+ if (show_tex) {
+ glBegin(GL_QUADS);
+ drawBoxTC(200,400, 200+BITMAP_W,300+BITMAP_H, 0,0,1,1);
+ glEnd();
+ } else {
+ glMatrixMode(GL_MODELVIEW);
+ glTranslatef(200,350,0);
+
+ if (translating)
+ x += fmod(translate_t*8,30);
+
+ if (rotating) {
+ glTranslatef(100,150,0);
+ glRotatef(rotate_t*2,0,0,1);
+ glTranslatef(-100,-150,0);
+ }
+ print(x,100, font, "This is a test");
+ print(x,130, font, "Now is the time for all good men to come to the aid of their country.");
+ print(x,160, font, "The quick brown fox jumps over the lazy dog.");
+ print(x,190, font, "0123456789");
+ }
+}
+
+void draw(void)
+{
+ draw_init();
+ draw_world();
+ stbwingraph_SwapBuffers(NULL);
+}
+
+static int initialized=0;
+static float last_dt;
+
+int move[4];
+int raw_mouse_x, raw_mouse_y;
+
+int loopmode(float dt, int real, int in_client)
+{
+ float actual_dt = dt;
+
+ if (!initialized) return 0;
+
+ rotate_t += dt;
+ translate_t += dt;
+
+// music_sim();
+ if (!real)
+ return 0;
+
+ if (dt > 0.25) dt = 0.25;
+ if (dt < 0.01) dt = 0.01;
+
+ draw();
+
+ return 0;
+}
+
+int winproc(void *data, stbwingraph_event *e)
+{
+ switch (e->type) {
+ case STBWGE_create:
+ break;
+
+ case STBWGE_char:
+ switch(e->key) {
+ case 27:
+ stbwingraph_ShowCursor(NULL,1);
+ return STBWINGRAPH_winproc_exit;
+ break;
+ case 'o': case 'O':
+ font = (font+1) % 3 + (font/3)*3;
+ break;
+ case 's': case 'S':
+ font = (font+3) % 6;
+ break;
+ case 't': case 'T':
+ translating = !translating;
+ translate_t = 0;
+ break;
+ case 'r': case 'R':
+ rotating = !rotating;
+ rotate_t = 0;
+ break;
+ case 'p': case 'P':
+ integer_align = !integer_align;
+ break;
+ case 'g': case 'G':
+ srgb = !srgb;
+ if (srgb)
+ glEnable(GL_FRAMEBUFFER_SRGB_EXT);
+ else
+ glDisable(GL_FRAMEBUFFER_SRGB_EXT);
+ break;
+ case 'v': case 'V':
+ show_tex = !show_tex;
+ break;
+ case 'b': case 'B':
+ black_on_white = !black_on_white;
+ break;
+ }
+ break;
+
+ case STBWGE_mousemove:
+ raw_mouse_x = e->mx;
+ raw_mouse_y = e->my;
+ break;
+
+#if 0
+ case STBWGE_mousewheel: do_mouse(e,0,0); break;
+ case STBWGE_leftdown: do_mouse(e, 1,0); break;
+ case STBWGE_leftup: do_mouse(e,-1,0); break;
+ case STBWGE_rightdown: do_mouse(e,0, 1); break;
+ case STBWGE_rightup: do_mouse(e,0,-1); break;
+#endif
+
+ case STBWGE_keydown:
+ if (e->key == VK_RIGHT) move[0] = 1;
+ if (e->key == VK_LEFT) move[1] = 1;
+ if (e->key == VK_UP) move[2] = 1;
+ if (e->key == VK_DOWN) move[3] = 1;
+ break;
+ case STBWGE_keyup:
+ if (e->key == VK_RIGHT) move[0] = 0;
+ if (e->key == VK_LEFT) move[1] = 0;
+ if (e->key == VK_UP) move[2] = 0;
+ if (e->key == VK_DOWN) move[3] = 0;
+ break;
+
+ case STBWGE_size:
+ sx = e->width;
+ sy = e->height;
+ loopmode(0,1,0);
+ break;
+
+ case STBWGE_draw:
+ if (initialized)
+ loopmode(0,1,0);
+ break;
+
+ default:
+ return STBWINGRAPH_unprocessed;
+ }
+ return 0;
+}
+
+void stbwingraph_main(void)
+{
+ stbwingraph_Priority(2);
+ stbwingraph_CreateWindow(1, winproc, NULL, "tt", SIZE_X,SIZE_Y, 0, 1, 0, 0);
+ stbwingraph_ShowCursor(NULL, 0);
+ load_fonts();
+ initialized = 1;
+ stbwingraph_MainLoop(loopmode, 0.016f); // 30 fps = 0.33
+}
+
diff --git a/thirdparty/stb/tests/oversample/oversample.dsp b/thirdparty/stb/tests/oversample/oversample.dsp
new file mode 100644
index 0000000..cc1edc3
--- /dev/null
+++ b/thirdparty/stb/tests/oversample/oversample.dsp
@@ -0,0 +1,97 @@
+# Microsoft Developer Studio Project File - Name="oversample" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=oversample - 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 "oversample.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 "oversample.mak" CFG="oversample - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "oversample - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "oversample - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "oversample - 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 "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /WX /GX /O2 /I "c:\sean\prj\stb" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# 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 /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# SUBTRACT LINK32 /map /debug
+
+!ELSEIF "$(CFG)" == "oversample - 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 "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /WX /Gm /GX /Zi /Od /I "c:\sean\prj\stb" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# 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 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "oversample - Win32 Release"
+# Name "oversample - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\main.c
+# End Source File
+# End Target
+# End Project
diff --git a/thirdparty/stb/tests/oversample/oversample.dsw b/thirdparty/stb/tests/oversample/oversample.dsw
new file mode 100644
index 0000000..0f5aa7f
--- /dev/null
+++ b/thirdparty/stb/tests/oversample/oversample.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "oversample"=.\oversample.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/thirdparty/stb/tests/oversample/oversample.exe b/thirdparty/stb/tests/oversample/oversample.exe
new file mode 100644
index 0000000..0040693
--- /dev/null
+++ b/thirdparty/stb/tests/oversample/oversample.exe
Binary files differ
diff --git a/thirdparty/stb/tests/oversample/stb_wingraph.h b/thirdparty/stb/tests/oversample/stb_wingraph.h
new file mode 100644
index 0000000..94798eb
--- /dev/null
+++ b/thirdparty/stb/tests/oversample/stb_wingraph.h
@@ -0,0 +1,829 @@
+// stb_wingraph.h v0.01 - public domain windows graphics programming
+// wraps WinMain, ChoosePixelFormat, ChangeDisplayResolution, etc. for
+// doing OpenGL graphics
+//
+// in ONE source file, put '#define STB_DEFINE' before including this
+// OR put '#define STB_WINMAIN' to define a WinMain that calls stbwingraph_main(void)
+//
+// @TODO:
+// 2d rendering interface (that can be done easily in software)
+// STB_WINGRAPH_SOFTWARE -- 2d software rendering only
+// STB_WINGRAPH_OPENGL -- OpenGL only
+
+
+#ifndef INCLUDE_STB_WINGRAPH_H
+#define INCLUDE_STB_WINGRAPH_H
+
+#ifdef STB_WINMAIN
+ #ifndef STB_DEFINE
+ #define STB_DEFINE
+ #define STB_WINGRAPH_DISABLE_DEFINE_AT_END
+ #endif
+#endif
+
+#ifdef STB_DEFINE
+ #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
+#define STB_EXTERN extern "C"
+#else
+#define STB_EXTERN
+#endif
+
+#ifdef STB_DEFINE
+#ifndef _WINDOWS_
+ #ifdef APIENTRY
+ #undef APIENTRY
+ #endif
+ #ifdef WINGDIAPI
+ #undef WINGDIAPI
+ #endif
+ #define _WIN32_WINNT 0x0400 // WM_MOUSEWHEEL
+ #include <windows.h>
+#endif
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#endif
+
+typedef void * stbwingraph_hwnd;
+typedef void * stbwingraph_hinstance;
+
+enum
+{
+ STBWINGRAPH_unprocessed = -(1 << 24),
+ STBWINGRAPH_do_not_show,
+ STBWINGRAPH_winproc_exit,
+ STBWINGRAPH_winproc_update,
+ STBWINGRAPH_update_exit,
+ STBWINGRAPH_update_pause,
+};
+
+typedef enum
+{
+ STBWGE__none=0,
+
+ STBWGE_create,
+ STBWGE_create_postshow,
+ STBWGE_draw,
+ STBWGE_destroy,
+ STBWGE_char,
+ STBWGE_keydown,
+ STBWGE_syskeydown,
+ STBWGE_keyup,
+ STBWGE_syskeyup,
+ STBWGE_deactivate,
+ STBWGE_activate,
+ STBWGE_size,
+
+ STBWGE_mousemove ,
+ STBWGE_leftdown , STBWGE_leftup ,
+ STBWGE_middledown, STBWGE_middleup,
+ STBWGE_rightdown , STBWGE_rightup ,
+ STBWGE_mousewheel,
+} stbwingraph_event_type;
+
+typedef struct
+{
+ stbwingraph_event_type type;
+
+ // for input events (mouse, keyboard)
+ int mx,my; // mouse x & y
+ int dx,dy;
+ int shift, ctrl, alt;
+
+ // for keyboard events
+ int key;
+
+ // for STBWGE_size:
+ int width, height;
+
+ // for STBWGE_crate
+ int did_share_lists; // if true, wglShareLists succeeded
+
+ void *handle;
+
+} stbwingraph_event;
+
+typedef int (*stbwingraph_window_proc)(void *data, stbwingraph_event *event);
+
+extern stbwingraph_hinstance stbwingraph_app;
+extern stbwingraph_hwnd stbwingraph_primary_window;
+extern int stbwingraph_request_fullscreen;
+extern int stbwingraph_request_windowed;
+
+STB_EXTERN void stbwingraph_ods(char *str, ...);
+STB_EXTERN int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type,
+ char *caption, char *text, ...);
+STB_EXTERN int stbwingraph_ChangeResolution(unsigned int w, unsigned int h,
+ unsigned int bits, int use_message_box);
+STB_EXTERN int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits,
+ int alpha_bits, int depth_bits, int stencil_bits, int accum_bits);
+STB_EXTERN int stbwingraph_DefineClass(void *hinstance, char *iconname);
+STB_EXTERN void stbwingraph_SwapBuffers(void *win);
+STB_EXTERN void stbwingraph_Priority(int n);
+
+STB_EXTERN void stbwingraph_MakeFonts(void *window, int font_base);
+STB_EXTERN void stbwingraph_ShowWindow(void *window);
+STB_EXTERN void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text, int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil);
+STB_EXTERN void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height);
+STB_EXTERN void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh);
+STB_EXTERN void stbwingraph_DestroyWindow(void *window);
+STB_EXTERN void stbwingraph_ShowCursor(void *window, int visible);
+STB_EXTERN float stbwingraph_GetTimestep(float minimum_time);
+STB_EXTERN void stbwingraph_SetGLWindow(void *win);
+typedef int (*stbwingraph_update)(float timestep, int real, int in_client);
+STB_EXTERN int stbwingraph_MainLoop(stbwingraph_update func, float mintime);
+
+#ifdef STB_DEFINE
+stbwingraph_hinstance stbwingraph_app;
+stbwingraph_hwnd stbwingraph_primary_window;
+int stbwingraph_request_fullscreen;
+int stbwingraph_request_windowed;
+
+void stbwingraph_ods(char *str, ...)
+{
+ char buffer[1024];
+ va_list v;
+ va_start(v,str);
+ vsprintf(buffer, str, v);
+ va_end(v);
+ OutputDebugString(buffer);
+}
+
+int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, char *caption, char *text, ...)
+{
+ va_list v;
+ char buffer[1024];
+ va_start(v, text);
+ vsprintf(buffer, text, v);
+ va_end(v);
+ return MessageBox(win, buffer, caption, type);
+}
+
+void stbwingraph_Priority(int n)
+{
+ int p;
+ switch (n) {
+ case -1: p = THREAD_PRIORITY_BELOW_NORMAL; break;
+ case 0: p = THREAD_PRIORITY_NORMAL; break;
+ case 1: p = THREAD_PRIORITY_ABOVE_NORMAL; break;
+ default:
+ if (n < 0) p = THREAD_PRIORITY_LOWEST;
+ else p = THREAD_PRIORITY_HIGHEST;
+ }
+ SetThreadPriority(GetCurrentThread(), p);
+}
+
+static void stbwingraph_ResetResolution(void)
+{
+ ChangeDisplaySettings(NULL, 0);
+}
+
+static void stbwingraph_RegisterResetResolution(void)
+{
+ static int done=0;
+ if (!done) {
+ done = 1;
+ atexit(stbwingraph_ResetResolution);
+ }
+}
+
+int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, unsigned int bits, int use_message_box)
+{
+ DEVMODE mode;
+ int res;
+
+ int i, tries=0;
+ for (i=0; ; ++i) {
+ int success = EnumDisplaySettings(NULL, i, &mode);
+ if (!success) break;
+ if (mode.dmBitsPerPel == bits && mode.dmPelsWidth == w && mode.dmPelsHeight == h) {
+ ++tries;
+ success = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
+ if (success == DISP_CHANGE_SUCCESSFUL) {
+ stbwingraph_RegisterResetResolution();
+ return TRUE;
+ }
+ break;
+ }
+ }
+
+ if (!tries) {
+ if (use_message_box)
+ stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
+ return FALSE;
+ }
+
+ // we tried but failed, so try explicitly doing it without specifying refresh rate
+
+ // Win95 support logic
+ mode.dmBitsPerPel = bits;
+ mode.dmPelsWidth = w;
+ mode.dmPelsHeight = h;
+ mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+
+ res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
+
+ switch (res) {
+ case DISP_CHANGE_SUCCESSFUL:
+ stbwingraph_RegisterResetResolution();
+ return TRUE;
+
+ case DISP_CHANGE_RESTART:
+ if (use_message_box)
+ stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "Please set your desktop to %d-bit color and then try again.");
+ return FALSE;
+
+ case DISP_CHANGE_FAILED:
+ if (use_message_box)
+ stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The hardware failed to change modes.");
+ return FALSE;
+
+ case DISP_CHANGE_BADMODE:
+ if (use_message_box)
+ stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
+ return FALSE;
+
+ default:
+ if (use_message_box)
+ stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "An unknown error prevented a change to a %d x %d x %d-bit display.", w, h, bits);
+ return FALSE;
+ }
+}
+
+int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, int alpha_bits, int depth_bits, int stencil_bits, int accum_bits)
+{
+ HDC dc = GetDC(win);
+ PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd) };
+ int pixel_format;
+
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+ pfd.dwLayerMask = PFD_MAIN_PLANE;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = color_bits;
+ pfd.cAlphaBits = alpha_bits;
+ pfd.cDepthBits = depth_bits;
+ pfd.cStencilBits = stencil_bits;
+ pfd.cAccumBits = accum_bits;
+
+ pixel_format = ChoosePixelFormat(dc, &pfd);
+ if (!pixel_format) return FALSE;
+
+ if (!DescribePixelFormat(dc, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd))
+ return FALSE;
+ SetPixelFormat(dc, pixel_format, &pfd);
+
+ return TRUE;
+}
+
+typedef struct
+{
+ // app data
+ stbwingraph_window_proc func;
+ void *data;
+ // creation parameters
+ int color, alpha, depth, stencil, accum;
+ HWND share_window;
+ HWND window;
+ // internal data
+ HGLRC rc;
+ HDC dc;
+ int hide_mouse;
+ int in_client;
+ int active;
+ int did_share_lists;
+ int mx,my; // last mouse positions
+} stbwingraph__window;
+
+static void stbwingraph__inclient(stbwingraph__window *win, int state)
+{
+ if (state != win->in_client) {
+ win->in_client = state;
+ if (win->hide_mouse)
+ ShowCursor(!state);
+ }
+}
+
+static void stbwingraph__key(stbwingraph_event *e, int type, int key, stbwingraph__window *z)
+{
+ e->type = type;
+ e->key = key;
+ e->shift = (GetKeyState(VK_SHIFT) < 0);
+ e->ctrl = (GetKeyState(VK_CONTROL) < 0);
+ e->alt = (GetKeyState(VK_MENU) < 0);
+ if (z) {
+ e->mx = z->mx;
+ e->my = z->my;
+ } else {
+ e->mx = e->my = 0;
+ }
+ e->dx = e->dy = 0;
+}
+
+static void stbwingraph__mouse(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
+{
+ static int captured = 0;
+ e->type = type;
+ e->mx = (short) LOWORD(lparam);
+ e->my = (short) HIWORD(lparam);
+ if (!z || z->mx == -(1 << 30)) {
+ e->dx = e->dy = 0;
+ } else {
+ e->dx = e->mx - z->mx;
+ e->dy = e->my - z->my;
+ }
+ e->shift = (wparam & MK_SHIFT) != 0;
+ e->ctrl = (wparam & MK_CONTROL) != 0;
+ e->alt = (wparam & MK_ALT) != 0;
+ if (z) {
+ z->mx = e->mx;
+ z->my = e->my;
+ }
+ if (capture) {
+ if (!captured && capture == 1)
+ SetCapture(wnd);
+ captured += capture;
+ if (!captured && capture == -1)
+ ReleaseCapture();
+ if (captured < 0) captured = 0;
+ }
+}
+
+static void stbwingraph__mousewheel(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
+{
+ // lparam seems bogus!
+ static int captured = 0;
+ e->type = type;
+ if (z) {
+ e->mx = z->mx;
+ e->my = z->my;
+ }
+ e->dx = e->dy = 0;
+ e->shift = (wparam & MK_SHIFT) != 0;
+ e->ctrl = (wparam & MK_CONTROL) != 0;
+ e->alt = (GetKeyState(VK_MENU) < 0);
+ e->key = ((int) wparam >> 16);
+}
+
+int stbwingraph_force_update;
+static int WINAPI stbwingraph_WinProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ int allow_default = TRUE;
+ stbwingraph_event e = { STBWGE__none };
+ // the following line is wrong for 64-bit windows, but VC6 doesn't have GetWindowLongPtr
+ stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(wnd, GWL_USERDATA);
+
+ switch (msg) {
+
+ case WM_CREATE:
+ {
+ LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lparam;
+ assert(z == NULL);
+ z = (stbwingraph__window *) lpcs->lpCreateParams;
+ SetWindowLong(wnd, GWL_USERDATA, (LONG) z);
+ z->dc = GetDC(wnd);
+ if (stbwingraph_SetPixelFormat(wnd, z->color, z->alpha, z->depth, z->stencil, z->accum)) {
+ z->rc = wglCreateContext(z->dc);
+ if (z->rc) {
+ e.type = STBWGE_create;
+ z->did_share_lists = FALSE;
+ if (z->share_window) {
+ stbwingraph__window *y = (stbwingraph__window *) GetWindowLong(z->share_window, GWL_USERDATA);
+ if (wglShareLists(z->rc, y->rc))
+ z->did_share_lists = TRUE;
+ }
+ wglMakeCurrent(z->dc, z->rc);
+ return 0;
+ }
+ }
+ return -1;
+ }
+
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(wnd, &ps);
+ SelectObject(hdc, GetStockObject(NULL_BRUSH));
+ e.type = STBWGE_draw;
+ e.handle = wnd;
+ z->func(z->data, &e);
+ EndPaint(wnd, &ps);
+ return 0;
+ }
+
+ case WM_DESTROY:
+ e.type = STBWGE_destroy;
+ e.handle = wnd;
+ if (z && z->func)
+ z->func(z->data, &e);
+ wglMakeCurrent(NULL, NULL) ;
+ if (z) {
+ if (z->rc) wglDeleteContext(z->rc);
+ z->dc = 0;
+ z->rc = 0;
+ }
+ if (wnd == stbwingraph_primary_window)
+ PostQuitMessage (0);
+ return 0;
+
+ case WM_CHAR: stbwingraph__key(&e, STBWGE_char , wparam, z); break;
+ case WM_KEYDOWN: stbwingraph__key(&e, STBWGE_keydown, wparam, z); break;
+ case WM_KEYUP: stbwingraph__key(&e, STBWGE_keyup , wparam, z); break;
+
+ case WM_NCMOUSEMOVE: stbwingraph__inclient(z,0); break;
+ case WM_MOUSEMOVE: stbwingraph__inclient(z,1); stbwingraph__mouse(&e, STBWGE_mousemove, wparam, lparam,0,wnd, z); break;
+ case WM_LBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_leftdown, wparam, lparam,1,wnd, z); break;
+ case WM_MBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_middledown, wparam, lparam,1,wnd, z); break;
+ case WM_RBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_rightdown, wparam, lparam,1,wnd, z); break;
+ case WM_LBUTTONUP: stbwingraph__mouse(&e, STBWGE_leftup, wparam, lparam,-1,wnd, z); break;
+ case WM_MBUTTONUP: stbwingraph__mouse(&e, STBWGE_middleup, wparam, lparam,-1,wnd, z); break;
+ case WM_RBUTTONUP: stbwingraph__mouse(&e, STBWGE_rightup, wparam, lparam,-1,wnd, z); break;
+ case WM_MOUSEWHEEL: stbwingraph__mousewheel(&e, STBWGE_mousewheel, wparam, lparam,0,wnd, z); break;
+
+ case WM_ACTIVATE:
+ allow_default = FALSE;
+ if (LOWORD(wparam)==WA_INACTIVE ) {
+ wglMakeCurrent(z->dc, NULL);
+ e.type = STBWGE_deactivate;
+ z->active = FALSE;
+ } else {
+ wglMakeCurrent(z->dc, z->rc);
+ e.type = STBWGE_activate;
+ z->active = TRUE;
+ }
+ e.handle = wnd;
+ z->func(z->data, &e);
+ return 0;
+
+ case WM_SIZE: {
+ RECT rect;
+ allow_default = FALSE;
+ GetClientRect(wnd, &rect);
+ e.type = STBWGE_size;
+ e.width = rect.right;
+ e.height = rect.bottom;
+ e.handle = wnd;
+ z->func(z->data, &e);
+ return 0;
+ }
+
+ default:
+ return DefWindowProc (wnd, msg, wparam, lparam);
+ }
+
+ if (e.type != STBWGE__none) {
+ int n;
+ e.handle = wnd;
+ n = z->func(z->data, &e);
+ if (n == STBWINGRAPH_winproc_exit) {
+ PostQuitMessage(0);
+ n = 0;
+ }
+ if (n == STBWINGRAPH_winproc_update) {
+ stbwingraph_force_update = TRUE;
+ return 1;
+ }
+ if (n != STBWINGRAPH_unprocessed)
+ return n;
+ }
+ return DefWindowProc (wnd, msg, wparam, lparam);
+}
+
+int stbwingraph_DefineClass(HINSTANCE hInstance, char *iconname)
+{
+ WNDCLASSEX wndclass;
+
+ stbwingraph_app = hInstance;
+
+ wndclass.cbSize = sizeof(wndclass);
+ wndclass.style = CS_OWNDC;
+ wndclass.lpfnWndProc = (WNDPROC) stbwingraph_WinProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = hInstance;
+ wndclass.hIcon = LoadIcon(hInstance, iconname);
+ wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
+ wndclass.hbrBackground = GetStockObject(NULL_BRUSH);
+ wndclass.lpszMenuName = "zwingraph";
+ wndclass.lpszClassName = "zwingraph";
+ wndclass.hIconSm = NULL;
+
+ if (!RegisterClassEx(&wndclass))
+ return FALSE;
+ return TRUE;
+}
+
+void stbwingraph_ShowWindow(void *window)
+{
+ stbwingraph_event e = { STBWGE_create_postshow };
+ stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
+ ShowWindow(window, SW_SHOWNORMAL);
+ InvalidateRect(window, NULL, TRUE);
+ UpdateWindow(window);
+ e.handle = window;
+ z->func(z->data, &e);
+}
+
+void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text,
+ int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil)
+{
+ HWND win;
+ DWORD dwstyle;
+ stbwingraph__window *z = (stbwingraph__window *) malloc(sizeof(*z));
+
+ if (z == NULL) return NULL;
+ memset(z, 0, sizeof(*z));
+ z->color = 24;
+ z->depth = 24;
+ z->alpha = dest_alpha;
+ z->stencil = stencil;
+ z->func = func;
+ z->data = data;
+ z->mx = -(1 << 30);
+ z->my = 0;
+
+ if (primary) {
+ if (stbwingraph_request_windowed)
+ fullscreen = FALSE;
+ else if (stbwingraph_request_fullscreen)
+ fullscreen = TRUE;
+ }
+
+ if (fullscreen) {
+ #ifdef STB_SIMPLE
+ stbwingraph_ChangeResolution(width, height, 32, 1);
+ #else
+ if (!stbwingraph_ChangeResolution(width, height, 32, 0))
+ return NULL;
+ #endif
+ dwstyle = WS_POPUP | WS_CLIPSIBLINGS;
+ } else {
+ RECT rect;
+ dwstyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
+ if (resizeable)
+ dwstyle |= WS_SIZEBOX | WS_MAXIMIZEBOX;
+ rect.top = 0;
+ rect.left = 0;
+ rect.right = width;
+ rect.bottom = height;
+ AdjustWindowRect(&rect, dwstyle, FALSE);
+ width = rect.right - rect.left;
+ height = rect.bottom - rect.top;
+ }
+
+ win = CreateWindow("zwingraph", text ? text : "sample", dwstyle,
+ CW_USEDEFAULT,0, width, height,
+ NULL, NULL, stbwingraph_app, z);
+
+ if (win == NULL) return win;
+
+ if (primary) {
+ if (stbwingraph_primary_window)
+ stbwingraph_DestroyWindow(stbwingraph_primary_window);
+ stbwingraph_primary_window = win;
+ }
+
+ {
+ stbwingraph_event e = { STBWGE_create };
+ stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
+ z->window = win;
+ e.did_share_lists = z->did_share_lists;
+ e.handle = win;
+ if (z->func(z->data, &e) != STBWINGRAPH_do_not_show)
+ stbwingraph_ShowWindow(win);
+ }
+
+ return win;
+}
+
+void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height)
+{
+ int fullscreen = 0;
+ #ifndef _DEBUG
+ if (width == 640 && height == 480) fullscreen = 1;
+ if (width == 800 && height == 600) fullscreen = 1;
+ if (width == 1024 && height == 768) fullscreen = 1;
+ if (width == 1280 && height == 1024) fullscreen = 1;
+ if (width == 1600 && height == 1200) fullscreen = 1;
+ //@TODO: widescreen widths
+ #endif
+ return stbwingraph_CreateWindow(1, func, NULL, NULL, width, height, fullscreen, 1, 0, 0);
+}
+
+void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh)
+{
+ if (fullscreen == -1) {
+ #ifdef _DEBUG
+ fullscreen = 0;
+ #else
+ fullscreen = 1;
+ #endif
+ }
+
+ if (fullscreen) {
+ if (fw) ww = fw;
+ if (fh) wh = fh;
+ }
+ return stbwingraph_CreateWindow(1, func, NULL, NULL, ww, wh, fullscreen, 1, 0, 0);
+}
+
+void stbwingraph_DestroyWindow(void *window)
+{
+ stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
+ DestroyWindow(window);
+ free(z);
+ if (stbwingraph_primary_window == window)
+ stbwingraph_primary_window = NULL;
+}
+
+void stbwingraph_ShowCursor(void *window, int visible)
+{
+ int hide;
+ stbwingraph__window *win;
+ if (!window)
+ window = stbwingraph_primary_window;
+ win = (stbwingraph__window *) GetWindowLong((HWND) window, GWL_USERDATA);
+ hide = !visible;
+ if (hide != win->hide_mouse) {
+ win->hide_mouse = hide;
+ if (!hide)
+ ShowCursor(TRUE);
+ else if (win->in_client)
+ ShowCursor(FALSE);
+ }
+}
+
+float stbwingraph_GetTimestep(float minimum_time)
+{
+ float elapsedTime;
+ double thisTime;
+ static double lastTime = -1;
+
+ if (lastTime == -1)
+ lastTime = timeGetTime() / 1000.0 - minimum_time;
+
+ for(;;) {
+ thisTime = timeGetTime() / 1000.0;
+ elapsedTime = (float) (thisTime - lastTime);
+ if (elapsedTime >= minimum_time) {
+ lastTime = thisTime;
+ return elapsedTime;
+ }
+ #if 1
+ Sleep(2);
+ #endif
+ }
+}
+
+void stbwingraph_SetGLWindow(void *win)
+{
+ stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
+ if (z)
+ wglMakeCurrent(z->dc, z->rc);
+}
+
+void stbwingraph_MakeFonts(void *window, int font_base)
+{
+ wglUseFontBitmaps(GetDC(window ? window : stbwingraph_primary_window), 0, 256, font_base);
+}
+
+// returns 1 if WM_QUIT, 0 if 'func' returned 0
+int stbwingraph_MainLoop(stbwingraph_update func, float mintime)
+{
+ int needs_drawing = FALSE;
+ MSG msg;
+
+ int is_animating = TRUE;
+ if (mintime <= 0) mintime = 0.01f;
+
+ for(;;) {
+ int n;
+
+ is_animating = TRUE;
+ // wait for a message if: (a) we're animating and there's already a message
+ // or (b) we're not animating
+ if (!is_animating || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+ stbwingraph_force_update = FALSE;
+ if (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ } else {
+ return 1; // WM_QUIT
+ }
+
+ // only force a draw for certain messages...
+ // if I don't do this, we peg at 50% for some reason... must
+ // be a bug somewhere, because we peg at 100% when rendering...
+ // very weird... looks like NVIDIA is pumping some messages
+ // through our pipeline? well, ok, I guess if we can get
+ // non-user-generated messages we have to do this
+ if (!stbwingraph_force_update) {
+ switch (msg.message) {
+ case WM_MOUSEMOVE:
+ case WM_NCMOUSEMOVE:
+ break;
+ case WM_CHAR:
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_TIMER:
+ case WM_SIZE:
+ case WM_ACTIVATE:
+ needs_drawing = TRUE;
+ break;
+ }
+ } else
+ needs_drawing = TRUE;
+ }
+
+ // if another message, process that first
+ // @TODO: i don't think this is working, because I can't key ahead
+ // in the SVT demo app
+ if (PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE))
+ continue;
+
+ // and now call update
+ if (needs_drawing || is_animating) {
+ int real=1, in_client=1;
+ if (stbwingraph_primary_window) {
+ stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(stbwingraph_primary_window, GWL_USERDATA);
+ if (z && !z->active) {
+ real = 0;
+ }
+ if (z)
+ in_client = z->in_client;
+ }
+
+ if (stbwingraph_primary_window)
+ stbwingraph_SetGLWindow(stbwingraph_primary_window);
+ n = func(stbwingraph_GetTimestep(mintime), real, in_client);
+ if (n == STBWINGRAPH_update_exit)
+ return 0; // update_quit
+
+ is_animating = (n != STBWINGRAPH_update_pause);
+
+ needs_drawing = FALSE;
+ }
+ }
+}
+
+void stbwingraph_SwapBuffers(void *win)
+{
+ stbwingraph__window *z;
+ if (win == NULL) win = stbwingraph_primary_window;
+ z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
+ if (z && z->dc)
+ SwapBuffers(z->dc);
+}
+#endif
+
+#ifdef STB_WINMAIN
+void stbwingraph_main(void);
+
+char *stb_wingraph_commandline;
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
+{
+ {
+ char buffer[1024];
+ // add spaces to either side of the string
+ buffer[0] = ' ';
+ strcpy(buffer+1, lpCmdLine);
+ strcat(buffer, " ");
+ if (strstr(buffer, " -reset ")) {
+ ChangeDisplaySettings(NULL, 0);
+ exit(0);
+ }
+ if (strstr(buffer, " -window ") || strstr(buffer, " -windowed "))
+ stbwingraph_request_windowed = TRUE;
+ else if (strstr(buffer, " -full ") || strstr(buffer, " -fullscreen "))
+ stbwingraph_request_fullscreen = TRUE;
+ }
+ stb_wingraph_commandline = lpCmdLine;
+
+ stbwingraph_DefineClass(hInstance, "appicon");
+ stbwingraph_main();
+
+ return 0;
+}
+#endif
+
+#undef STB_EXTERN
+#ifdef STB_WINGRAPH_DISABLE_DEFINE_AT_END
+#undef STB_DEFINE
+#endif
+
+#endif // INCLUDE_STB_WINGRAPH_H