aboutsummaryrefslogtreecommitdiff
path: root/subprojects/d2tk/nanovg/example
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/d2tk/nanovg/example')
-rw-r--r--subprojects/d2tk/nanovg/example/LICENSE_OFL.txt92
-rw-r--r--subprojects/d2tk/nanovg/example/NotoEmoji-Regular.ttfbin0 -> 418804 bytes
-rwxr-xr-xsubprojects/d2tk/nanovg/example/Roboto-Bold.ttfbin0 -> 135820 bytes
-rwxr-xr-xsubprojects/d2tk/nanovg/example/Roboto-Light.ttfbin0 -> 140276 bytes
-rwxr-xr-xsubprojects/d2tk/nanovg/example/Roboto-Regular.ttfbin0 -> 145348 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/demo.c1226
-rw-r--r--subprojects/d2tk/nanovg/example/demo.h26
-rw-r--r--subprojects/d2tk/nanovg/example/entypo.ttfbin0 -> 35392 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/example_fbo.c272
-rw-r--r--subprojects/d2tk/nanovg/example/example_gl2.c162
-rw-r--r--subprojects/d2tk/nanovg/example/example_gl3.c198
-rw-r--r--subprojects/d2tk/nanovg/example/example_gles2.c155
-rw-r--r--subprojects/d2tk/nanovg/example/example_gles3.c155
-rw-r--r--subprojects/d2tk/nanovg/example/images.txt13
-rw-r--r--subprojects/d2tk/nanovg/example/images/image1.jpgbin0 -> 25760 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image10.jpgbin0 -> 3439 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image11.jpgbin0 -> 3818 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image12.jpgbin0 -> 5452 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image2.jpgbin0 -> 24091 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image3.jpgbin0 -> 29282 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image4.jpgbin0 -> 23830 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image5.jpgbin0 -> 27131 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image6.jpgbin0 -> 25116 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image7.jpgbin0 -> 25590 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image8.jpgbin0 -> 24607 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image9.jpgbin0 -> 4035 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/perf.c186
-rw-r--r--subprojects/d2tk/nanovg/example/perf.h46
-rw-r--r--subprojects/d2tk/nanovg/example/screenshot-01.pngbin0 -> 989424 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/screenshot-02.pngbin0 -> 229443 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/stb_image_write.h511
31 files changed, 3042 insertions, 0 deletions
diff --git a/subprojects/d2tk/nanovg/example/LICENSE_OFL.txt b/subprojects/d2tk/nanovg/example/LICENSE_OFL.txt
new file mode 100644
index 0000000..d952d62
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/LICENSE_OFL.txt
@@ -0,0 +1,92 @@
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/subprojects/d2tk/nanovg/example/NotoEmoji-Regular.ttf b/subprojects/d2tk/nanovg/example/NotoEmoji-Regular.ttf
new file mode 100644
index 0000000..19b7bad
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/NotoEmoji-Regular.ttf
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/Roboto-Bold.ttf b/subprojects/d2tk/nanovg/example/Roboto-Bold.ttf
new file mode 100755
index 0000000..aaf374d
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/Roboto-Bold.ttf
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/Roboto-Light.ttf b/subprojects/d2tk/nanovg/example/Roboto-Light.ttf
new file mode 100755
index 0000000..664e1b2
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/Roboto-Light.ttf
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/Roboto-Regular.ttf b/subprojects/d2tk/nanovg/example/Roboto-Regular.ttf
new file mode 100755
index 0000000..3e6e2e7
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/Roboto-Regular.ttf
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/demo.c b/subprojects/d2tk/nanovg/example/demo.c
new file mode 100644
index 0000000..cfad99e
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/demo.c
@@ -0,0 +1,1226 @@
+#include "demo.h"
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#ifdef NANOVG_GLEW
+# include <GL/glew.h>
+#endif
+#include <GLFW/glfw3.h>
+#include "nanovg.h"
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "stb_image_write.h"
+
+
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#elif !defined(__MINGW32__)
+#include <iconv.h>
+#endif
+
+#define ICON_SEARCH 0x1F50D
+#define ICON_CIRCLED_CROSS 0x2716
+#define ICON_CHEVRON_RIGHT 0xE75E
+#define ICON_CHECK 0x2713
+#define ICON_LOGIN 0xE740
+#define ICON_TRASH 0xE729
+
+//static float minf(float a, float b) { return a < b ? a : b; }
+static float maxf(float a, float b) { return a > b ? a : b; }
+//static float absf(float a) { return a >= 0.0f ? a : -a; }
+static float clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
+
+// Returns 1 if col.rgba is 0.0f,0.0f,0.0f,0.0f, 0 otherwise
+int isBlack(NVGcolor col)
+{
+ if( col.r == 0.0f && col.g == 0.0f && col.b == 0.0f && col.a == 0.0f )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+static char* cpToUTF8(int cp, char* str)
+{
+ int n = 0;
+ if (cp < 0x80) n = 1;
+ else if (cp < 0x800) n = 2;
+ else if (cp < 0x10000) n = 3;
+ else if (cp < 0x200000) n = 4;
+ else if (cp < 0x4000000) n = 5;
+ else if (cp <= 0x7fffffff) n = 6;
+ str[n] = '\0';
+ switch (n) {
+ case 6: str[5] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x4000000;
+ case 5: str[4] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x200000;
+ case 4: str[3] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x10000;
+ case 3: str[2] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x800;
+ case 2: str[1] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0xc0;
+ case 1: str[0] = cp;
+ }
+ return str;
+}
+
+
+void drawWindow(NVGcontext* vg, const char* title, float x, float y, float w, float h)
+{
+ float cornerRadius = 3.0f;
+ NVGpaint shadowPaint;
+ NVGpaint headerPaint;
+
+ nvgSave(vg);
+// nvgClearState(vg);
+
+ // Window
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x,y, w,h, cornerRadius);
+ nvgFillColor(vg, nvgRGBA(28,30,34,192));
+// nvgFillColor(vg, nvgRGBA(0,0,0,128));
+ nvgFill(vg);
+
+ // Drop shadow
+ shadowPaint = nvgBoxGradient(vg, x,y+2, w,h, cornerRadius*2, 10, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
+ nvgBeginPath(vg);
+ nvgRect(vg, x-10,y-10, w+20,h+30);
+ nvgRoundedRect(vg, x,y, w,h, cornerRadius);
+ nvgPathWinding(vg, NVG_HOLE);
+ nvgFillPaint(vg, shadowPaint);
+ nvgFill(vg);
+
+ // Header
+ headerPaint = nvgLinearGradient(vg, x,y,x,y+15, nvgRGBA(255,255,255,8), nvgRGBA(0,0,0,16));
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+1,y+1, w-2,30, cornerRadius-1);
+ nvgFillPaint(vg, headerPaint);
+ nvgFill(vg);
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, x+0.5f, y+0.5f+30);
+ nvgLineTo(vg, x+0.5f+w-1, y+0.5f+30);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,32));
+ nvgStroke(vg);
+
+ nvgFontSize(vg, 18.0f);
+ nvgFontFace(vg, "sans-bold");
+ nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
+
+ nvgFontBlur(vg,2);
+ nvgFillColor(vg, nvgRGBA(0,0,0,128));
+ nvgText(vg, x+w/2,y+16+1, title, NULL);
+
+ nvgFontBlur(vg,0);
+ nvgFillColor(vg, nvgRGBA(220,220,220,160));
+ nvgText(vg, x+w/2,y+16, title, NULL);
+
+ nvgRestore(vg);
+}
+
+void drawSearchBox(NVGcontext* vg, const char* text, float x, float y, float w, float h)
+{
+ NVGpaint bg;
+ char icon[8];
+ float cornerRadius = h/2-1;
+
+ // Edit
+ bg = nvgBoxGradient(vg, x,y+1.5f, w,h, h/2,5, nvgRGBA(0,0,0,16), nvgRGBA(0,0,0,92));
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x,y, w,h, cornerRadius);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+/* nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
+ nvgStroke(vg);*/
+
+ nvgFontSize(vg, h*1.3f);
+ nvgFontFace(vg, "icons");
+ nvgFillColor(vg, nvgRGBA(255,255,255,64));
+ nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+h*0.55f, y+h*0.55f, cpToUTF8(ICON_SEARCH,icon), NULL);
+
+ nvgFontSize(vg, 20.0f);
+ nvgFontFace(vg, "sans");
+ nvgFillColor(vg, nvgRGBA(255,255,255,32));
+
+ nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+h*1.05f,y+h*0.5f,text, NULL);
+
+ nvgFontSize(vg, h*1.3f);
+ nvgFontFace(vg, "icons");
+ nvgFillColor(vg, nvgRGBA(255,255,255,32));
+ nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+w-h*0.55f, y+h*0.55f, cpToUTF8(ICON_CIRCLED_CROSS,icon), NULL);
+}
+
+void drawDropDown(NVGcontext* vg, const char* text, float x, float y, float w, float h)
+{
+ NVGpaint bg;
+ char icon[8];
+ float cornerRadius = 4.0f;
+
+ bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16));
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
+ nvgStroke(vg);
+
+ nvgFontSize(vg, 20.0f);
+ nvgFontFace(vg, "sans");
+ nvgFillColor(vg, nvgRGBA(255,255,255,160));
+ nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+h*0.3f,y+h*0.5f,text, NULL);
+
+ nvgFontSize(vg, h*1.3f);
+ nvgFontFace(vg, "icons");
+ nvgFillColor(vg, nvgRGBA(255,255,255,64));
+ nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+w-h*0.5f, y+h*0.5f, cpToUTF8(ICON_CHEVRON_RIGHT,icon), NULL);
+}
+
+void drawLabel(NVGcontext* vg, const char* text, float x, float y, float w, float h)
+{
+ NVG_NOTUSED(w);
+
+ nvgFontSize(vg, 18.0f);
+ nvgFontFace(vg, "sans");
+ nvgFillColor(vg, nvgRGBA(255,255,255,128));
+
+ nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x,y+h*0.5f,text, NULL);
+}
+
+void drawEditBoxBase(NVGcontext* vg, float x, float y, float w, float h)
+{
+ NVGpaint bg;
+ // Edit
+ bg = nvgBoxGradient(vg, x+1,y+1+1.5f, w-2,h-2, 3,4, nvgRGBA(255,255,255,32), nvgRGBA(32,32,32,32));
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+1,y+1, w-2,h-2, 4-1);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, 4-0.5f);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
+ nvgStroke(vg);
+}
+
+void drawEditBox(NVGcontext* vg, const char* text, float x, float y, float w, float h)
+{
+
+ drawEditBoxBase(vg, x,y, w,h);
+
+ nvgFontSize(vg, 20.0f);
+ nvgFontFace(vg, "sans");
+ nvgFillColor(vg, nvgRGBA(255,255,255,64));
+ nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+h*0.3f,y+h*0.5f,text, NULL);
+}
+
+void drawEditBoxNum(NVGcontext* vg,
+ const char* text, const char* units, float x, float y, float w, float h)
+{
+ float uw;
+
+ drawEditBoxBase(vg, x,y, w,h);
+
+ uw = nvgTextBounds(vg, 0,0, units, NULL, NULL);
+
+ nvgFontSize(vg, 18.0f);
+ nvgFontFace(vg, "sans");
+ nvgFillColor(vg, nvgRGBA(255,255,255,64));
+ nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+w-h*0.3f,y+h*0.5f,units, NULL);
+
+ nvgFontSize(vg, 20.0f);
+ nvgFontFace(vg, "sans");
+ nvgFillColor(vg, nvgRGBA(255,255,255,128));
+ nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+w-uw-h*0.5f,y+h*0.5f,text, NULL);
+}
+
+void drawCheckBox(NVGcontext* vg, const char* text, float x, float y, float w, float h)
+{
+ NVGpaint bg;
+ char icon[8];
+ NVG_NOTUSED(w);
+
+ nvgFontSize(vg, 18.0f);
+ nvgFontFace(vg, "sans");
+ nvgFillColor(vg, nvgRGBA(255,255,255,160));
+
+ nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+28,y+h*0.5f,text, NULL);
+
+ bg = nvgBoxGradient(vg, x+1,y+(int)(h*0.5f)-9+1, 18,18, 3,3, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,92));
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+1,y+(int)(h*0.5f)-9, 18,18, 3);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+ nvgFontSize(vg, 40);
+ nvgFontFace(vg, "icons");
+ nvgFillColor(vg, nvgRGBA(255,255,255,128));
+ nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+9+2, y+h*0.5f, cpToUTF8(ICON_CHECK,icon), NULL);
+}
+
+void drawButton(NVGcontext* vg, int preicon, const char* text, float x, float y, float w, float h, NVGcolor col)
+{
+ NVGpaint bg;
+ char icon[8];
+ float cornerRadius = 4.0f;
+ float tw = 0, iw = 0;
+
+ bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,isBlack(col)?16:32), nvgRGBA(0,0,0,isBlack(col)?16:32));
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1);
+ if (!isBlack(col)) {
+ nvgFillColor(vg, col);
+ nvgFill(vg);
+ }
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
+ nvgStroke(vg);
+
+ nvgFontSize(vg, 20.0f);
+ nvgFontFace(vg, "sans-bold");
+ tw = nvgTextBounds(vg, 0,0, text, NULL, NULL);
+ if (preicon != 0) {
+ nvgFontSize(vg, h*1.3f);
+ nvgFontFace(vg, "icons");
+ iw = nvgTextBounds(vg, 0,0, cpToUTF8(preicon,icon), NULL, NULL);
+ iw += h*0.15f;
+ }
+
+ if (preicon != 0) {
+ nvgFontSize(vg, h*1.3f);
+ nvgFontFace(vg, "icons");
+ nvgFillColor(vg, nvgRGBA(255,255,255,96));
+ nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
+ nvgText(vg, x+w*0.5f-tw*0.5f-iw*0.75f, y+h*0.5f, cpToUTF8(preicon,icon), NULL);
+ }
+
+ nvgFontSize(vg, 20.0f);
+ nvgFontFace(vg, "sans-bold");
+ nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
+ nvgFillColor(vg, nvgRGBA(0,0,0,160));
+ nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f-1,text, NULL);
+ nvgFillColor(vg, nvgRGBA(255,255,255,160));
+ nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f,text, NULL);
+}
+
+void drawSlider(NVGcontext* vg, float pos, float x, float y, float w, float h)
+{
+ NVGpaint bg, knob;
+ float cy = y+(int)(h*0.5f);
+ float kr = (int)(h*0.25f);
+
+ nvgSave(vg);
+// nvgClearState(vg);
+
+ // Slot
+ bg = nvgBoxGradient(vg, x,cy-2+1, w,4, 2,2, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,128));
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x,cy-2, w,4, 2);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+ // Knob Shadow
+ bg = nvgRadialGradient(vg, x+(int)(pos*w),cy+1, kr-3,kr+3, nvgRGBA(0,0,0,64), nvgRGBA(0,0,0,0));
+ nvgBeginPath(vg);
+ nvgRect(vg, x+(int)(pos*w)-kr-5,cy-kr-5,kr*2+5+5,kr*2+5+5+3);
+ nvgCircle(vg, x+(int)(pos*w),cy, kr);
+ nvgPathWinding(vg, NVG_HOLE);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+ // Knob
+ knob = nvgLinearGradient(vg, x,cy-kr,x,cy+kr, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16));
+ nvgBeginPath(vg);
+ nvgCircle(vg, x+(int)(pos*w),cy, kr-1);
+ nvgFillColor(vg, nvgRGBA(40,43,48,255));
+ nvgFill(vg);
+ nvgFillPaint(vg, knob);
+ nvgFill(vg);
+
+ nvgBeginPath(vg);
+ nvgCircle(vg, x+(int)(pos*w),cy, kr-0.5f);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,92));
+ nvgStroke(vg);
+
+ nvgRestore(vg);
+}
+
+void drawEyes(NVGcontext* vg, float x, float y, float w, float h, float mx, float my, float t)
+{
+ NVGpaint gloss, bg;
+ float ex = w *0.23f;
+ float ey = h * 0.5f;
+ float lx = x + ex;
+ float ly = y + ey;
+ float rx = x + w - ex;
+ float ry = y + ey;
+ float dx,dy,d;
+ float br = (ex < ey ? ex : ey) * 0.5f;
+ float blink = 1 - pow(sinf(t*0.5f),200)*0.8f;
+
+ bg = nvgLinearGradient(vg, x,y+h*0.5f,x+w*0.1f,y+h, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,16));
+ nvgBeginPath(vg);
+ nvgEllipse(vg, lx+3.0f,ly+16.0f, ex,ey);
+ nvgEllipse(vg, rx+3.0f,ry+16.0f, ex,ey);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+ bg = nvgLinearGradient(vg, x,y+h*0.25f,x+w*0.1f,y+h, nvgRGBA(220,220,220,255), nvgRGBA(128,128,128,255));
+ nvgBeginPath(vg);
+ nvgEllipse(vg, lx,ly, ex,ey);
+ nvgEllipse(vg, rx,ry, ex,ey);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+ dx = (mx - rx) / (ex * 10);
+ dy = (my - ry) / (ey * 10);
+ d = sqrtf(dx*dx+dy*dy);
+ if (d > 1.0f) {
+ dx /= d; dy /= d;
+ }
+ dx *= ex*0.4f;
+ dy *= ey*0.5f;
+ nvgBeginPath(vg);
+ nvgEllipse(vg, lx+dx,ly+dy+ey*0.25f*(1-blink), br,br*blink);
+ nvgFillColor(vg, nvgRGBA(32,32,32,255));
+ nvgFill(vg);
+
+ dx = (mx - rx) / (ex * 10);
+ dy = (my - ry) / (ey * 10);
+ d = sqrtf(dx*dx+dy*dy);
+ if (d > 1.0f) {
+ dx /= d; dy /= d;
+ }
+ dx *= ex*0.4f;
+ dy *= ey*0.5f;
+ nvgBeginPath(vg);
+ nvgEllipse(vg, rx+dx,ry+dy+ey*0.25f*(1-blink), br,br*blink);
+ nvgFillColor(vg, nvgRGBA(32,32,32,255));
+ nvgFill(vg);
+
+ gloss = nvgRadialGradient(vg, lx-ex*0.25f,ly-ey*0.5f, ex*0.1f,ex*0.75f, nvgRGBA(255,255,255,128), nvgRGBA(255,255,255,0));
+ nvgBeginPath(vg);
+ nvgEllipse(vg, lx,ly, ex,ey);
+ nvgFillPaint(vg, gloss);
+ nvgFill(vg);
+
+ gloss = nvgRadialGradient(vg, rx-ex*0.25f,ry-ey*0.5f, ex*0.1f,ex*0.75f, nvgRGBA(255,255,255,128), nvgRGBA(255,255,255,0));
+ nvgBeginPath(vg);
+ nvgEllipse(vg, rx,ry, ex,ey);
+ nvgFillPaint(vg, gloss);
+ nvgFill(vg);
+}
+
+void drawGraph(NVGcontext* vg, float x, float y, float w, float h, float t)
+{
+ NVGpaint bg;
+ float samples[6];
+ float sx[6], sy[6];
+ float dx = w/5.0f;
+ int i;
+
+ samples[0] = (1+sinf(t*1.2345f+cosf(t*0.33457f)*0.44f))*0.5f;
+ samples[1] = (1+sinf(t*0.68363f+cosf(t*1.3f)*1.55f))*0.5f;
+ samples[2] = (1+sinf(t*1.1642f+cosf(t*0.33457)*1.24f))*0.5f;
+ samples[3] = (1+sinf(t*0.56345f+cosf(t*1.63f)*0.14f))*0.5f;
+ samples[4] = (1+sinf(t*1.6245f+cosf(t*0.254f)*0.3f))*0.5f;
+ samples[5] = (1+sinf(t*0.345f+cosf(t*0.03f)*0.6f))*0.5f;
+
+ for (i = 0; i < 6; i++) {
+ sx[i] = x+i*dx;
+ sy[i] = y+h*samples[i]*0.8f;
+ }
+
+ // Graph background
+ bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(0,160,192,0), nvgRGBA(0,160,192,64));
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, sx[0], sy[0]);
+ for (i = 1; i < 6; i++)
+ nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
+ nvgLineTo(vg, x+w, y+h);
+ nvgLineTo(vg, x, y+h);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+
+ // Graph line
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, sx[0], sy[0]+2);
+ for (i = 1; i < 6; i++)
+ nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1]+2, sx[i]-dx*0.5f,sy[i]+2, sx[i],sy[i]+2);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,32));
+ nvgStrokeWidth(vg, 3.0f);
+ nvgStroke(vg);
+
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, sx[0], sy[0]);
+ for (i = 1; i < 6; i++)
+ nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
+ nvgStrokeColor(vg, nvgRGBA(0,160,192,255));
+ nvgStrokeWidth(vg, 3.0f);
+ nvgStroke(vg);
+
+ // Graph sample pos
+ for (i = 0; i < 6; i++) {
+ bg = nvgRadialGradient(vg, sx[i],sy[i]+2, 3.0f,8.0f, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,0));
+ nvgBeginPath(vg);
+ nvgRect(vg, sx[i]-10, sy[i]-10+2, 20,20);
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+ }
+
+ nvgBeginPath(vg);
+ for (i = 0; i < 6; i++)
+ nvgCircle(vg, sx[i], sy[i], 4.0f);
+ nvgFillColor(vg, nvgRGBA(0,160,192,255));
+ nvgFill(vg);
+ nvgBeginPath(vg);
+ for (i = 0; i < 6; i++)
+ nvgCircle(vg, sx[i], sy[i], 2.0f);
+ nvgFillColor(vg, nvgRGBA(220,220,220,255));
+ nvgFill(vg);
+
+ nvgStrokeWidth(vg, 1.0f);
+}
+
+void drawSpinner(NVGcontext* vg, float cx, float cy, float r, float t)
+{
+ float a0 = 0.0f + t*6;
+ float a1 = NVG_PI + t*6;
+ float r0 = r;
+ float r1 = r * 0.75f;
+ float ax,ay, bx,by;
+ NVGpaint paint;
+
+ nvgSave(vg);
+
+ nvgBeginPath(vg);
+ nvgArc(vg, cx,cy, r0, a0, a1, NVG_CW);
+ nvgArc(vg, cx,cy, r1, a1, a0, NVG_CCW);
+ nvgClosePath(vg);
+ ax = cx + cosf(a0) * (r0+r1)*0.5f;
+ ay = cy + sinf(a0) * (r0+r1)*0.5f;
+ bx = cx + cosf(a1) * (r0+r1)*0.5f;
+ by = cy + sinf(a1) * (r0+r1)*0.5f;
+ paint = nvgLinearGradient(vg, ax,ay, bx,by, nvgRGBA(0,0,0,0), nvgRGBA(0,0,0,128));
+ nvgFillPaint(vg, paint);
+ nvgFill(vg);
+
+ nvgRestore(vg);
+}
+
+void drawThumbnails(NVGcontext* vg, float x, float y, float w, float h, const int* images, int nimages, float t)
+{
+ float cornerRadius = 3.0f;
+ NVGpaint shadowPaint, imgPaint, fadePaint;
+ float ix,iy,iw,ih;
+ float thumb = 60.0f;
+ float arry = 30.5f;
+ int imgw, imgh;
+ float stackh = (nimages/2) * (thumb+10) + 10;
+ int i;
+ float u = (1+cosf(t*0.5f))*0.5f;
+ float u2 = (1-cosf(t*0.2f))*0.5f;
+ float scrollh, dv;
+
+ nvgSave(vg);
+// nvgClearState(vg);
+
+ // Drop shadow
+ shadowPaint = nvgBoxGradient(vg, x,y+4, w,h, cornerRadius*2, 20, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
+ nvgBeginPath(vg);
+ nvgRect(vg, x-10,y-10, w+20,h+30);
+ nvgRoundedRect(vg, x,y, w,h, cornerRadius);
+ nvgPathWinding(vg, NVG_HOLE);
+ nvgFillPaint(vg, shadowPaint);
+ nvgFill(vg);
+
+ // Window
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x,y, w,h, cornerRadius);
+ nvgMoveTo(vg, x-10,y+arry);
+ nvgLineTo(vg, x+1,y+arry-11);
+ nvgLineTo(vg, x+1,y+arry+11);
+ nvgFillColor(vg, nvgRGBA(200,200,200,255));
+ nvgFill(vg);
+
+ nvgSave(vg);
+ nvgScissor(vg, x,y,w,h);
+ nvgTranslate(vg, 0, -(stackh - h)*u);
+
+ dv = 1.0f / (float)(nimages-1);
+
+ for (i = 0; i < nimages; i++) {
+ float tx, ty, v, a;
+ tx = x+10;
+ ty = y+10;
+ tx += (i%2) * (thumb+10);
+ ty += (i/2) * (thumb+10);
+ nvgImageSize(vg, images[i], &imgw, &imgh);
+ if (imgw < imgh) {
+ iw = thumb;
+ ih = iw * (float)imgh/(float)imgw;
+ ix = 0;
+ iy = -(ih-thumb)*0.5f;
+ } else {
+ ih = thumb;
+ iw = ih * (float)imgw/(float)imgh;
+ ix = -(iw-thumb)*0.5f;
+ iy = 0;
+ }
+
+ v = i * dv;
+ a = clampf((u2-v) / dv, 0, 1);
+
+ if (a < 1.0f)
+ drawSpinner(vg, tx+thumb/2,ty+thumb/2, thumb*0.25f, t);
+
+ imgPaint = nvgImagePattern(vg, tx+ix, ty+iy, iw,ih, 0.0f/180.0f*NVG_PI, images[i], a);
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, tx,ty, thumb,thumb, 5);
+ nvgFillPaint(vg, imgPaint);
+ nvgFill(vg);
+
+ shadowPaint = nvgBoxGradient(vg, tx-1,ty, thumb+2,thumb+2, 5, 3, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
+ nvgBeginPath(vg);
+ nvgRect(vg, tx-5,ty-5, thumb+10,thumb+10);
+ nvgRoundedRect(vg, tx,ty, thumb,thumb, 6);
+ nvgPathWinding(vg, NVG_HOLE);
+ nvgFillPaint(vg, shadowPaint);
+ nvgFill(vg);
+
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, tx+0.5f,ty+0.5f, thumb-1,thumb-1, 4-0.5f);
+ nvgStrokeWidth(vg,1.0f);
+ nvgStrokeColor(vg, nvgRGBA(255,255,255,192));
+ nvgStroke(vg);
+ }
+ nvgRestore(vg);
+
+ // Hide fades
+ fadePaint = nvgLinearGradient(vg, x,y,x,y+6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0));
+ nvgBeginPath(vg);
+ nvgRect(vg, x+4,y,w-8,6);
+ nvgFillPaint(vg, fadePaint);
+ nvgFill(vg);
+
+ fadePaint = nvgLinearGradient(vg, x,y+h,x,y+h-6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0));
+ nvgBeginPath(vg);
+ nvgRect(vg, x+4,y+h-6,w-8,6);
+ nvgFillPaint(vg, fadePaint);
+ nvgFill(vg);
+
+ // Scroll bar
+ shadowPaint = nvgBoxGradient(vg, x+w-12+1,y+4+1, 8,h-8, 3,4, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,92));
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+w-12,y+4, 8,h-8, 3);
+ nvgFillPaint(vg, shadowPaint);
+// nvgFillColor(vg, nvgRGBA(255,0,0,128));
+ nvgFill(vg);
+
+ scrollh = (h/stackh) * (h-8);
+ shadowPaint = nvgBoxGradient(vg, x+w-12-1,y+4+(h-8-scrollh)*u-1, 8,scrollh, 3,4, nvgRGBA(220,220,220,255), nvgRGBA(128,128,128,255));
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x+w-12+1,y+4+1 + (h-8-scrollh)*u, 8-2,scrollh-2, 2);
+ nvgFillPaint(vg, shadowPaint);
+// nvgFillColor(vg, nvgRGBA(0,0,0,128));
+ nvgFill(vg);
+
+ nvgRestore(vg);
+}
+
+void drawColorwheel(NVGcontext* vg, float x, float y, float w, float h, float t)
+{
+ int i;
+ float r0, r1, ax,ay, bx,by, cx,cy, aeps, r;
+ float hue = sinf(t * 0.12f);
+ NVGpaint paint;
+
+ nvgSave(vg);
+
+/* nvgBeginPath(vg);
+ nvgRect(vg, x,y,w,h);
+ nvgFillColor(vg, nvgRGBA(255,0,0,128));
+ nvgFill(vg);*/
+
+ cx = x + w*0.5f;
+ cy = y + h*0.5f;
+ r1 = (w < h ? w : h) * 0.5f - 5.0f;
+ r0 = r1 - 20.0f;
+ aeps = 0.5f / r1; // half a pixel arc length in radians (2pi cancels out).
+
+ for (i = 0; i < 6; i++) {
+ float a0 = (float)i / 6.0f * NVG_PI * 2.0f - aeps;
+ float a1 = (float)(i+1.0f) / 6.0f * NVG_PI * 2.0f + aeps;
+ nvgBeginPath(vg);
+ nvgArc(vg, cx,cy, r0, a0, a1, NVG_CW);
+ nvgArc(vg, cx,cy, r1, a1, a0, NVG_CCW);
+ nvgClosePath(vg);
+ ax = cx + cosf(a0) * (r0+r1)*0.5f;
+ ay = cy + sinf(a0) * (r0+r1)*0.5f;
+ bx = cx + cosf(a1) * (r0+r1)*0.5f;
+ by = cy + sinf(a1) * (r0+r1)*0.5f;
+ paint = nvgLinearGradient(vg, ax,ay, bx,by, nvgHSLA(a0/(NVG_PI*2),1.0f,0.55f,255), nvgHSLA(a1/(NVG_PI*2),1.0f,0.55f,255));
+ nvgFillPaint(vg, paint);
+ nvgFill(vg);
+ }
+
+ nvgBeginPath(vg);
+ nvgCircle(vg, cx,cy, r0-0.5f);
+ nvgCircle(vg, cx,cy, r1+0.5f);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,64));
+ nvgStrokeWidth(vg, 1.0f);
+ nvgStroke(vg);
+
+ // Selector
+ nvgSave(vg);
+ nvgTranslate(vg, cx,cy);
+ nvgRotate(vg, hue*NVG_PI*2);
+
+ // Marker on
+ nvgStrokeWidth(vg, 2.0f);
+ nvgBeginPath(vg);
+ nvgRect(vg, r0-1,-3,r1-r0+2,6);
+ nvgStrokeColor(vg, nvgRGBA(255,255,255,192));
+ nvgStroke(vg);
+
+ paint = nvgBoxGradient(vg, r0-3,-5,r1-r0+6,10, 2,4, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
+ nvgBeginPath(vg);
+ nvgRect(vg, r0-2-10,-4-10,r1-r0+4+20,8+20);
+ nvgRect(vg, r0-2,-4,r1-r0+4,8);
+ nvgPathWinding(vg, NVG_HOLE);
+ nvgFillPaint(vg, paint);
+ nvgFill(vg);
+
+ // Center triangle
+ r = r0 - 6;
+ ax = cosf(120.0f/180.0f*NVG_PI) * r;
+ ay = sinf(120.0f/180.0f*NVG_PI) * r;
+ bx = cosf(-120.0f/180.0f*NVG_PI) * r;
+ by = sinf(-120.0f/180.0f*NVG_PI) * r;
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, r,0);
+ nvgLineTo(vg, ax,ay);
+ nvgLineTo(vg, bx,by);
+ nvgClosePath(vg);
+ paint = nvgLinearGradient(vg, r,0, ax,ay, nvgHSLA(hue,1.0f,0.5f,255), nvgRGBA(255,255,255,255));
+ nvgFillPaint(vg, paint);
+ nvgFill(vg);
+ paint = nvgLinearGradient(vg, (r+ax)*0.5f,(0+ay)*0.5f, bx,by, nvgRGBA(0,0,0,0), nvgRGBA(0,0,0,255));
+ nvgFillPaint(vg, paint);
+ nvgFill(vg);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,64));
+ nvgStroke(vg);
+
+ // Select circle on triangle
+ ax = cosf(120.0f/180.0f*NVG_PI) * r*0.3f;
+ ay = sinf(120.0f/180.0f*NVG_PI) * r*0.4f;
+ nvgStrokeWidth(vg, 2.0f);
+ nvgBeginPath(vg);
+ nvgCircle(vg, ax,ay,5);
+ nvgStrokeColor(vg, nvgRGBA(255,255,255,192));
+ nvgStroke(vg);
+
+ paint = nvgRadialGradient(vg, ax,ay, 7,9, nvgRGBA(0,0,0,64), nvgRGBA(0,0,0,0));
+ nvgBeginPath(vg);
+ nvgRect(vg, ax-20,ay-20,40,40);
+ nvgCircle(vg, ax,ay,7);
+ nvgPathWinding(vg, NVG_HOLE);
+ nvgFillPaint(vg, paint);
+ nvgFill(vg);
+
+ nvgRestore(vg);
+
+ nvgRestore(vg);
+}
+
+void drawLines(NVGcontext* vg, float x, float y, float w, float h, float t)
+{
+ int i, j;
+ float pad = 5.0f, s = w/9.0f - pad*2;
+ float pts[4*2], fx, fy;
+ int joins[3] = {NVG_MITER, NVG_ROUND, NVG_BEVEL};
+ int caps[3] = {NVG_BUTT, NVG_ROUND, NVG_SQUARE};
+ NVG_NOTUSED(h);
+
+ nvgSave(vg);
+ pts[0] = -s*0.25f + cosf(t*0.3f) * s*0.5f;
+ pts[1] = sinf(t*0.3f) * s*0.5f;
+ pts[2] = -s*0.25;
+ pts[3] = 0;
+ pts[4] = s*0.25f;
+ pts[5] = 0;
+ pts[6] = s*0.25f + cosf(-t*0.3f) * s*0.5f;
+ pts[7] = sinf(-t*0.3f) * s*0.5f;
+
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ fx = x + s*0.5f + (i*3+j)/9.0f*w + pad;
+ fy = y - s*0.5f + pad;
+
+ nvgLineCap(vg, caps[i]);
+ nvgLineJoin(vg, joins[j]);
+
+ nvgStrokeWidth(vg, s*0.3f);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,160));
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, fx+pts[0], fy+pts[1]);
+ nvgLineTo(vg, fx+pts[2], fy+pts[3]);
+ nvgLineTo(vg, fx+pts[4], fy+pts[5]);
+ nvgLineTo(vg, fx+pts[6], fy+pts[7]);
+ nvgStroke(vg);
+
+ nvgLineCap(vg, NVG_BUTT);
+ nvgLineJoin(vg, NVG_BEVEL);
+
+ nvgStrokeWidth(vg, 1.0f);
+ nvgStrokeColor(vg, nvgRGBA(0,192,255,255));
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, fx+pts[0], fy+pts[1]);
+ nvgLineTo(vg, fx+pts[2], fy+pts[3]);
+ nvgLineTo(vg, fx+pts[4], fy+pts[5]);
+ nvgLineTo(vg, fx+pts[6], fy+pts[7]);
+ nvgStroke(vg);
+ }
+ }
+
+
+ nvgRestore(vg);
+}
+
+int loadDemoData(NVGcontext* vg, DemoData* data)
+{
+ int i;
+
+ if (vg == NULL)
+ return -1;
+
+ for (i = 0; i < 12; i++) {
+ char file[128];
+ snprintf(file, 128, "../example/images/image%d.jpg", i+1);
+ data->images[i] = nvgCreateImage(vg, file, 0);
+ if (data->images[i] == 0) {
+ printf("Could not load %s.\n", file);
+ return -1;
+ }
+ }
+
+ data->fontIcons = nvgCreateFont(vg, "icons", "../example/entypo.ttf");
+ if (data->fontIcons == -1) {
+ printf("Could not add font icons.\n");
+ return -1;
+ }
+ data->fontNormal = nvgCreateFont(vg, "sans", "../example/Roboto-Regular.ttf");
+ if (data->fontNormal == -1) {
+ printf("Could not add font italic.\n");
+ return -1;
+ }
+ data->fontBold = nvgCreateFont(vg, "sans-bold", "../example/Roboto-Bold.ttf");
+ if (data->fontBold == -1) {
+ printf("Could not add font bold.\n");
+ return -1;
+ }
+ data->fontEmoji = nvgCreateFont(vg, "emoji", "../example/NotoEmoji-Regular.ttf");
+ if (data->fontEmoji == -1) {
+ printf("Could not add font emoji.\n");
+ return -1;
+ }
+ nvgAddFallbackFontId(vg, data->fontNormal, data->fontEmoji);
+ nvgAddFallbackFontId(vg, data->fontBold, data->fontEmoji);
+
+ return 0;
+}
+
+void freeDemoData(NVGcontext* vg, DemoData* data)
+{
+ int i;
+
+ if (vg == NULL)
+ return;
+
+ for (i = 0; i < 12; i++)
+ nvgDeleteImage(vg, data->images[i]);
+}
+
+void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, float mx, float my)
+{
+ NVGtextRow rows[3];
+ NVGglyphPosition glyphs[100];
+ const char* text = "This is longer chunk of text.\n \n Would have used lorem ipsum but she was busy jumping over the lazy dog with the fox and all the men who came to the aid of the party.🎉";
+ const char* start;
+ const char* end;
+ int nrows, i, nglyphs, j, lnum = 0;
+ float lineh;
+ float caretx, px;
+ float bounds[4];
+ float a;
+ float gx,gy;
+ int gutter = 0;
+ NVG_NOTUSED(height);
+
+ nvgSave(vg);
+
+ nvgFontSize(vg, 18.0f);
+ nvgFontFace(vg, "sans");
+ nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP);
+ nvgTextMetrics(vg, NULL, NULL, &lineh);
+
+ // The text break API can be used to fill a large buffer of rows,
+ // or to iterate over the text just few lines (or just one) at a time.
+ // The "next" variable of the last returned item tells where to continue.
+ start = text;
+ end = text + strlen(text);
+ while ((nrows = nvgTextBreakLines(vg, start, end, width, rows, 3))) {
+ for (i = 0; i < nrows; i++) {
+ NVGtextRow* row = &rows[i];
+ int hit = mx > x && mx < (x+width) && my >= y && my < (y+lineh);
+
+ nvgBeginPath(vg);
+ nvgFillColor(vg, nvgRGBA(255,255,255,hit?64:16));
+ nvgRect(vg, x, y, row->width, lineh);
+ nvgFill(vg);
+
+ nvgFillColor(vg, nvgRGBA(255,255,255,255));
+ nvgText(vg, x, y, row->start, row->end);
+
+ if (hit) {
+ caretx = (mx < x+row->width/2) ? x : x+row->width;
+ px = x;
+ nglyphs = nvgTextGlyphPositions(vg, x, y, row->start, row->end, glyphs, 100);
+ for (j = 0; j < nglyphs; j++) {
+ float x0 = glyphs[j].x;
+ float x1 = (j+1 < nglyphs) ? glyphs[j+1].x : x+row->width;
+ float gx = x0 * 0.3f + x1 * 0.7f;
+ if (mx >= px && mx < gx)
+ caretx = glyphs[j].x;
+ px = gx;
+ }
+ nvgBeginPath(vg);
+ nvgFillColor(vg, nvgRGBA(255,192,0,255));
+ nvgRect(vg, caretx, y, 1, lineh);
+ nvgFill(vg);
+
+ gutter = lnum+1;
+ gx = x - 10;
+ gy = y + lineh/2;
+ }
+ lnum++;
+ y += lineh;
+ }
+ // Keep going...
+ start = rows[nrows-1].next;
+ }
+
+ if (gutter) {
+ char txt[16];
+ snprintf(txt, sizeof(txt), "%d", gutter);
+ nvgFontSize(vg, 13.0f);
+ nvgTextAlign(vg, NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
+
+ nvgTextBounds(vg, gx,gy, txt, NULL, bounds);
+
+ nvgBeginPath(vg);
+ nvgFillColor(vg, nvgRGBA(255,192,0,255));
+ nvgRoundedRect(vg, (int)bounds[0]-4,(int)bounds[1]-2, (int)(bounds[2]-bounds[0])+8, (int)(bounds[3]-bounds[1])+4, ((int)(bounds[3]-bounds[1])+4)/2-1);
+ nvgFill(vg);
+
+ nvgFillColor(vg, nvgRGBA(32,32,32,255));
+ nvgText(vg, gx,gy, txt, NULL);
+ }
+
+ y += 20.0f;
+
+ nvgFontSize(vg, 13.0f);
+ nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP);
+ nvgTextLineHeight(vg, 1.2f);
+
+ nvgTextBoxBounds(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL, bounds);
+
+ // Fade the tooltip out when close to it.
+ gx = fabsf((mx - (bounds[0]+bounds[2])*0.5f) / (bounds[0] - bounds[2]));
+ gy = fabsf((my - (bounds[1]+bounds[3])*0.5f) / (bounds[1] - bounds[3]));
+ a = maxf(gx, gy) - 0.5f;
+ a = clampf(a, 0, 1);
+ nvgGlobalAlpha(vg, a);
+
+ nvgBeginPath(vg);
+ nvgFillColor(vg, nvgRGBA(220,220,220,255));
+ nvgRoundedRect(vg, bounds[0]-2,bounds[1]-2, (int)(bounds[2]-bounds[0])+4, (int)(bounds[3]-bounds[1])+4, 3);
+ px = (int)((bounds[2]+bounds[0])/2);
+ nvgMoveTo(vg, px,bounds[1] - 10);
+ nvgLineTo(vg, px+7,bounds[1]+1);
+ nvgLineTo(vg, px-7,bounds[1]+1);
+ nvgFill(vg);
+
+ nvgFillColor(vg, nvgRGBA(0,0,0,220));
+ nvgTextBox(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL);
+
+ nvgRestore(vg);
+}
+
+void drawWidths(NVGcontext* vg, float x, float y, float width)
+{
+ int i;
+
+ nvgSave(vg);
+
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,255));
+
+ for (i = 0; i < 20; i++) {
+ float w = (i+0.5f)*0.1f;
+ nvgStrokeWidth(vg, w);
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, x,y);
+ nvgLineTo(vg, x+width,y+width*0.3f);
+ nvgStroke(vg);
+ y += 10;
+ }
+
+ nvgRestore(vg);
+}
+
+void drawCaps(NVGcontext* vg, float x, float y, float width)
+{
+ int i;
+ int caps[3] = {NVG_BUTT, NVG_ROUND, NVG_SQUARE};
+ float lineWidth = 8.0f;
+
+ nvgSave(vg);
+
+ nvgBeginPath(vg);
+ nvgRect(vg, x-lineWidth/2, y, width+lineWidth, 40);
+ nvgFillColor(vg, nvgRGBA(255,255,255,32));
+ nvgFill(vg);
+
+ nvgBeginPath(vg);
+ nvgRect(vg, x, y, width, 40);
+ nvgFillColor(vg, nvgRGBA(255,255,255,32));
+ nvgFill(vg);
+
+ nvgStrokeWidth(vg, lineWidth);
+ for (i = 0; i < 3; i++) {
+ nvgLineCap(vg, caps[i]);
+ nvgStrokeColor(vg, nvgRGBA(0,0,0,255));
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, x, y + i*10 + 5);
+ nvgLineTo(vg, x+width, y + i*10 + 5);
+ nvgStroke(vg);
+ }
+
+ nvgRestore(vg);
+}
+
+void drawScissor(NVGcontext* vg, float x, float y, float t)
+{
+ nvgSave(vg);
+
+ // Draw first rect and set scissor to it's area.
+ nvgTranslate(vg, x, y);
+ nvgRotate(vg, nvgDegToRad(5));
+ nvgBeginPath(vg);
+ nvgRect(vg, -20,-20,60,40);
+ nvgFillColor(vg, nvgRGBA(255,0,0,255));
+ nvgFill(vg);
+ nvgScissor(vg, -20,-20,60,40);
+
+ // Draw second rectangle with offset and rotation.
+ nvgTranslate(vg, 40,0);
+ nvgRotate(vg, t);
+
+ // Draw the intended second rectangle without any scissoring.
+ nvgSave(vg);
+ nvgResetScissor(vg);
+ nvgBeginPath(vg);
+ nvgRect(vg, -20,-10,60,30);
+ nvgFillColor(vg, nvgRGBA(255,128,0,64));
+ nvgFill(vg);
+ nvgRestore(vg);
+
+ // Draw second rectangle with combined scissoring.
+ nvgIntersectScissor(vg, -20,-10,60,30);
+ nvgBeginPath(vg);
+ nvgRect(vg, -20,-10,60,30);
+ nvgFillColor(vg, nvgRGBA(255,128,0,255));
+ nvgFill(vg);
+
+ nvgRestore(vg);
+}
+
+void renderDemo(NVGcontext* vg, float mx, float my, float width, float height,
+ float t, int blowup, DemoData* data)
+{
+ float x,y,popy;
+
+ drawEyes(vg, width - 250, 50, 150, 100, mx, my, t);
+ drawParagraph(vg, width - 450, 50, 150, 100, mx, my);
+ drawGraph(vg, 0, height/2, width, height/2, t);
+ drawColorwheel(vg, width - 300, height - 300, 250.0f, 250.0f, t);
+
+ // Line joints
+ drawLines(vg, 120, height-50, 600, 50, t);
+
+ // Line caps
+ drawWidths(vg, 10, 50, 30);
+
+ // Line caps
+ drawCaps(vg, 10, 300, 30);
+
+ drawScissor(vg, 50, height-80, t);
+
+ nvgSave(vg);
+ if (blowup) {
+ nvgRotate(vg, sinf(t*0.3f)*5.0f/180.0f*NVG_PI);
+ nvgScale(vg, 2.0f, 2.0f);
+ }
+
+ // Widgets
+ drawWindow(vg, "Widgets `n Stuff", 50, 50, 300, 400);
+ x = 60; y = 95;
+ drawSearchBox(vg, "Search", x,y,280,25);
+ y += 40;
+ drawDropDown(vg, "Effects", x,y,280,28);
+ popy = y + 14;
+ y += 45;
+
+ // Form
+ drawLabel(vg, "Login", x,y, 280,20);
+ y += 25;
+ drawEditBox(vg, "Email", x,y, 280,28);
+ y += 35;
+ drawEditBox(vg, "Password", x,y, 280,28);
+ y += 38;
+ drawCheckBox(vg, "Remember me", x,y, 140,28);
+ drawButton(vg, ICON_LOGIN, "Sign in", x+138, y, 140, 28, nvgRGBA(0,96,128,255));
+ y += 45;
+
+ // Slider
+ drawLabel(vg, "Diameter", x,y, 280,20);
+ y += 25;
+ drawEditBoxNum(vg, "123.00", "px", x+180,y, 100,28);
+ drawSlider(vg, 0.4f, x,y, 170,28);
+ y += 55;
+
+ drawButton(vg, ICON_TRASH, "Delete", x, y, 160, 28, nvgRGBA(128,16,8,255));
+ drawButton(vg, 0, "Cancel", x+170, y, 110, 28, nvgRGBA(0,0,0,0));
+
+ // Thumbnails box
+ drawThumbnails(vg, 365, popy-30, 160, 300, data->images, 12, t);
+
+ nvgRestore(vg);
+}
+
+static int mini(int a, int b) { return a < b ? a : b; }
+
+static void unpremultiplyAlpha(unsigned char* image, int w, int h, int stride)
+{
+ int x,y;
+
+ // Unpremultiply
+ for (y = 0; y < h; y++) {
+ unsigned char *row = &image[y*stride];
+ for (x = 0; x < w; x++) {
+ int r = row[0], g = row[1], b = row[2], a = row[3];
+ if (a != 0) {
+ row[0] = (int)mini(r*255/a, 255);
+ row[1] = (int)mini(g*255/a, 255);
+ row[2] = (int)mini(b*255/a, 255);
+ }
+ row += 4;
+ }
+ }
+
+ // Defringe
+ for (y = 0; y < h; y++) {
+ unsigned char *row = &image[y*stride];
+ for (x = 0; x < w; x++) {
+ int r = 0, g = 0, b = 0, a = row[3], n = 0;
+ if (a == 0) {
+ if (x-1 > 0 && row[-1] != 0) {
+ r += row[-4];
+ g += row[-3];
+ b += row[-2];
+ n++;
+ }
+ if (x+1 < w && row[7] != 0) {
+ r += row[4];
+ g += row[5];
+ b += row[6];
+ n++;
+ }
+ if (y-1 > 0 && row[-stride+3] != 0) {
+ r += row[-stride];
+ g += row[-stride+1];
+ b += row[-stride+2];
+ n++;
+ }
+ if (y+1 < h && row[stride+3] != 0) {
+ r += row[stride];
+ g += row[stride+1];
+ b += row[stride+2];
+ n++;
+ }
+ if (n > 0) {
+ row[0] = r/n;
+ row[1] = g/n;
+ row[2] = b/n;
+ }
+ }
+ row += 4;
+ }
+ }
+}
+
+static void setAlpha(unsigned char* image, int w, int h, int stride, unsigned char a)
+{
+ int x, y;
+ for (y = 0; y < h; y++) {
+ unsigned char* row = &image[y*stride];
+ for (x = 0; x < w; x++)
+ row[x*4+3] = a;
+ }
+}
+
+static void flipHorizontal(unsigned char* image, int w, int h, int stride)
+{
+ int i = 0, j = h-1, k;
+ while (i < j) {
+ unsigned char* ri = &image[i * stride];
+ unsigned char* rj = &image[j * stride];
+ for (k = 0; k < w*4; k++) {
+ unsigned char t = ri[k];
+ ri[k] = rj[k];
+ rj[k] = t;
+ }
+ i++;
+ j--;
+ }
+}
+
+void saveScreenShot(int w, int h, int premult, const char* name)
+{
+ unsigned char* image = (unsigned char*)malloc(w*h*4);
+ if (image == NULL)
+ return;
+ glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image);
+ if (premult)
+ unpremultiplyAlpha(image, w, h, w*4);
+ else
+ setAlpha(image, w, h, w*4, 255);
+ flipHorizontal(image, w, h, w*4);
+ stbi_write_png(name, w, h, 4, image, w*4);
+ free(image);
+}
diff --git a/subprojects/d2tk/nanovg/example/demo.h b/subprojects/d2tk/nanovg/example/demo.h
new file mode 100644
index 0000000..aace449
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/demo.h
@@ -0,0 +1,26 @@
+#ifndef DEMO_H
+#define DEMO_H
+
+#include "nanovg.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct DemoData {
+ int fontNormal, fontBold, fontIcons, fontEmoji;
+ int images[12];
+};
+typedef struct DemoData DemoData;
+
+int loadDemoData(NVGcontext* vg, DemoData* data);
+void freeDemoData(NVGcontext* vg, DemoData* data);
+void renderDemo(NVGcontext* vg, float mx, float my, float width, float height, float t, int blowup, DemoData* data);
+
+void saveScreenShot(int w, int h, int premult, const char* name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // DEMO_H
diff --git a/subprojects/d2tk/nanovg/example/entypo.ttf b/subprojects/d2tk/nanovg/example/entypo.ttf
new file mode 100644
index 0000000..fc305d2
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/entypo.ttf
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/example_fbo.c b/subprojects/d2tk/nanovg/example/example_fbo.c
new file mode 100644
index 0000000..cff4ed2
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/example_fbo.c
@@ -0,0 +1,272 @@
+//
+// Copyright (c) 2013 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <stdio.h>
+#ifdef NANOVG_GLEW
+# include <GL/glew.h>
+#endif
+#ifdef __APPLE__
+# define GLFW_INCLUDE_GLCOREARB
+#endif
+#include <GLFW/glfw3.h>
+#include "nanovg.h"
+#define NANOVG_GL3_IMPLEMENTATION
+#include "nanovg_gl.h"
+#include "nanovg_gl_utils.h"
+#include "perf.h"
+
+void renderPattern(NVGcontext* vg, NVGLUframebuffer* fb, float t, float pxRatio)
+{
+ int winWidth, winHeight;
+ int fboWidth, fboHeight;
+ int pw, ph, x, y;
+ float s = 20.0f;
+ float sr = (cosf(t)+1)*0.5f;
+ float r = s * 0.6f * (0.2f + 0.8f * sr);
+
+ if (fb == NULL) return;
+
+ nvgImageSize(vg, fb->image, &fboWidth, &fboHeight);
+ winWidth = (int)(fboWidth / pxRatio);
+ winHeight = (int)(fboHeight / pxRatio);
+
+ // Draw some stuff to an FBO as a test
+ nvgluBindFramebuffer(fb);
+ glViewport(0, 0, fboWidth, fboHeight);
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
+ nvgBeginFrame(vg, winWidth, winHeight, pxRatio);
+
+ pw = (int)ceilf(winWidth / s);
+ ph = (int)ceilf(winHeight / s);
+
+ nvgBeginPath(vg);
+ for (y = 0; y < ph; y++) {
+ for (x = 0; x < pw; x++) {
+ float cx = (x+0.5f) * s;
+ float cy = (y+0.5f) * s;
+ nvgCircle(vg, cx,cy, r);
+ }
+ }
+ nvgFillColor(vg, nvgRGBA(220,160,0,200));
+ nvgFill(vg);
+
+ nvgEndFrame(vg);
+ nvgluBindFramebuffer(NULL);
+}
+
+int loadFonts(NVGcontext* vg)
+{
+ int font;
+ font = nvgCreateFont(vg, "sans", "../example/Roboto-Regular.ttf");
+ if (font == -1) {
+ printf("Could not add font regular.\n");
+ return -1;
+ }
+ font = nvgCreateFont(vg, "sans-bold", "../example/Roboto-Bold.ttf");
+ if (font == -1) {
+ printf("Could not add font bold.\n");
+ return -1;
+ }
+ return 0;
+}
+
+void errorcb(int error, const char* desc)
+{
+ printf("GLFW error %d: %s\n", error, desc);
+}
+
+static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ NVG_NOTUSED(scancode);
+ NVG_NOTUSED(mods);
+ if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
+ glfwSetWindowShouldClose(window, GL_TRUE);
+}
+
+int main()
+{
+ GLFWwindow* window;
+ NVGcontext* vg = NULL;
+ GPUtimer gpuTimer;
+ PerfGraph fps, cpuGraph, gpuGraph;
+ double prevt = 0, cpuTime = 0;
+ NVGLUframebuffer* fb = NULL;
+ int winWidth, winHeight;
+ int fbWidth, fbHeight;
+ float pxRatio;
+
+ if (!glfwInit()) {
+ printf("Failed to init GLFW.");
+ return -1;
+ }
+
+ initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time");
+ initGraph(&cpuGraph, GRAPH_RENDER_MS, "CPU Time");
+ initGraph(&gpuGraph, GRAPH_RENDER_MS, "GPU Time");
+
+ glfwSetErrorCallback(errorcb);
+#ifndef _WIN32 // don't require this on win32, and works with more cards
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+#endif
+ glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1);
+
+#ifdef DEMO_MSAA
+ glfwWindowHint(GLFW_SAMPLES, 4);
+#endif
+ window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL);
+// window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL);
+ if (!window) {
+ glfwTerminate();
+ return -1;
+ }
+
+ glfwSetKeyCallback(window, key);
+
+ glfwMakeContextCurrent(window);
+#ifdef NANOVG_GLEW
+ glewExperimental = GL_TRUE;
+ if(glewInit() != GLEW_OK) {
+ printf("Could not init glew.\n");
+ return -1;
+ }
+ // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here.
+ glGetError();
+#endif
+
+#ifdef DEMO_MSAA
+ vg = nvgCreateGL3(NVG_STENCIL_STROKES | NVG_DEBUG);
+#else
+ vg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG);
+#endif
+ if (vg == NULL) {
+ printf("Could not init nanovg.\n");
+ return -1;
+ }
+
+ // Create hi-dpi FBO for hi-dpi screens.
+ glfwGetWindowSize(window, &winWidth, &winHeight);
+ glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
+ // Calculate pixel ration for hi-dpi devices.
+ pxRatio = (float)fbWidth / (float)winWidth;
+
+ // The image pattern is tiled, set repeat on x and y.
+ fb = nvgluCreateFramebuffer(vg, (int)(100*pxRatio), (int)(100*pxRatio), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
+ if (fb == NULL) {
+ printf("Could not create FBO.\n");
+ return -1;
+ }
+
+ if (loadFonts(vg) == -1) {
+ printf("Could not load fonts\n");
+ return -1;
+ }
+
+ glfwSwapInterval(0);
+
+ initGPUTimer(&gpuTimer);
+
+ glfwSetTime(0);
+ prevt = glfwGetTime();
+
+ while (!glfwWindowShouldClose(window))
+ {
+ double mx, my, t, dt;
+ float gpuTimes[3];
+ int i, n;
+
+ t = glfwGetTime();
+ dt = t - prevt;
+ prevt = t;
+
+ startGPUTimer(&gpuTimer);
+
+ glfwGetCursorPos(window, &mx, &my);
+ glfwGetWindowSize(window, &winWidth, &winHeight);
+ glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
+ // Calculate pixel ration for hi-dpi devices.
+ pxRatio = (float)fbWidth / (float)winWidth;
+
+ renderPattern(vg, fb, t, pxRatio);
+
+ // Update and render
+ glViewport(0, 0, fbWidth, fbHeight);
+ glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
+
+ nvgBeginFrame(vg, winWidth, winHeight, pxRatio);
+
+ // Use the FBO as image pattern.
+ if (fb != NULL) {
+ NVGpaint img = nvgImagePattern(vg, 0, 0, 100, 100, 0, fb->image, 1.0f);
+ nvgSave(vg);
+
+ for (i = 0; i < 20; i++) {
+ nvgBeginPath(vg);
+ nvgRect(vg, 10 + i*30,10, 10, winHeight-20);
+ nvgFillColor(vg, nvgHSLA(i/19.0f, 0.5f, 0.5f, 255));
+ nvgFill(vg);
+ }
+
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, 140 + sinf(t*1.3f)*100, 140 + cosf(t*1.71244f)*100, 250, 250, 20);
+ nvgFillPaint(vg, img);
+ nvgFill(vg);
+ nvgStrokeColor(vg, nvgRGBA(220,160,0,255));
+ nvgStrokeWidth(vg, 3.0f);
+ nvgStroke(vg);
+
+ nvgRestore(vg);
+ }
+
+ renderGraph(vg, 5,5, &fps);
+ renderGraph(vg, 5+200+5,5, &cpuGraph);
+ if (gpuTimer.supported)
+ renderGraph(vg, 5+200+5+200+5,5, &gpuGraph);
+
+ nvgEndFrame(vg);
+
+ // Measure the CPU time taken excluding swap buffers (as the swap may wait for GPU)
+ cpuTime = glfwGetTime() - t;
+
+ updateGraph(&fps, dt);
+ updateGraph(&cpuGraph, cpuTime);
+
+ // We may get multiple results.
+ n = stopGPUTimer(&gpuTimer, gpuTimes, 3);
+ for (i = 0; i < n; i++)
+ updateGraph(&gpuGraph, gpuTimes[i]);
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ nvgluDeleteFramebuffer(fb);
+
+ nvgDeleteGL3(vg);
+
+ printf("Average Frame Time: %.2f ms\n", getGraphAverage(&fps) * 1000.0f);
+ printf(" CPU Time: %.2f ms\n", getGraphAverage(&cpuGraph) * 1000.0f);
+ printf(" GPU Time: %.2f ms\n", getGraphAverage(&gpuGraph) * 1000.0f);
+
+ glfwTerminate();
+ return 0;
+}
diff --git a/subprojects/d2tk/nanovg/example/example_gl2.c b/subprojects/d2tk/nanovg/example/example_gl2.c
new file mode 100644
index 0000000..7fd5621
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/example_gl2.c
@@ -0,0 +1,162 @@
+//
+// Copyright (c) 2013 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <stdio.h>
+#ifdef NANOVG_GLEW
+# include <GL/glew.h>
+#endif
+#define GLFW_INCLUDE_GLEXT
+#include <GLFW/glfw3.h>
+#include "nanovg.h"
+#define NANOVG_GL2_IMPLEMENTATION
+#include "nanovg_gl.h"
+#include "demo.h"
+#include "perf.h"
+
+
+void errorcb(int error, const char* desc)
+{
+ printf("GLFW error %d: %s\n", error, desc);
+}
+
+int blowup = 0;
+int screenshot = 0;
+int premult = 0;
+
+static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ NVG_NOTUSED(scancode);
+ NVG_NOTUSED(mods);
+ if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
+ glfwSetWindowShouldClose(window, GL_TRUE);
+ if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
+ blowup = !blowup;
+ if (key == GLFW_KEY_S && action == GLFW_PRESS)
+ screenshot = 1;
+ if (key == GLFW_KEY_P && action == GLFW_PRESS)
+ premult = !premult;
+}
+
+int main()
+{
+ GLFWwindow* window;
+ DemoData data;
+ NVGcontext* vg = NULL;
+ PerfGraph fps;
+ double prevt = 0;
+
+ if (!glfwInit()) {
+ printf("Failed to init GLFW.");
+ return -1;
+ }
+
+ initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time");
+
+ glfwSetErrorCallback(errorcb);
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+#ifdef DEMO_MSAA
+ glfwWindowHint(GLFW_SAMPLES, 4);
+#endif
+
+ window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL);
+// window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL);
+ if (!window) {
+ glfwTerminate();
+ return -1;
+ }
+
+ glfwSetKeyCallback(window, key);
+
+ glfwMakeContextCurrent(window);
+#ifdef NANOVG_GLEW
+ if(glewInit() != GLEW_OK) {
+ printf("Could not init glew.\n");
+ return -1;
+ }
+#endif
+
+#ifdef DEMO_MSAA
+ vg = nvgCreateGL2(NVG_STENCIL_STROKES | NVG_DEBUG);
+#else
+ vg = nvgCreateGL2(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG);
+#endif
+ if (vg == NULL) {
+ printf("Could not init nanovg.\n");
+ return -1;
+ }
+
+ if (loadDemoData(vg, &data) == -1)
+ return -1;
+
+ glfwSwapInterval(0);
+
+ glfwSetTime(0);
+ prevt = glfwGetTime();
+
+ while (!glfwWindowShouldClose(window))
+ {
+ double mx, my, t, dt;
+ int winWidth, winHeight;
+ int fbWidth, fbHeight;
+ float pxRatio;
+
+ t = glfwGetTime();
+ dt = t - prevt;
+ prevt = t;
+ updateGraph(&fps, dt);
+
+ glfwGetCursorPos(window, &mx, &my);
+ glfwGetWindowSize(window, &winWidth, &winHeight);
+ glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
+
+ // Calculate pixel ration for hi-dpi devices.
+ pxRatio = (float)fbWidth / (float)winWidth;
+
+ // Update and render
+ glViewport(0, 0, fbWidth, fbHeight);
+ if (premult)
+ glClearColor(0,0,0,0);
+ else
+ glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
+
+ nvgBeginFrame(vg, winWidth, winHeight, pxRatio);
+
+ renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data);
+ renderGraph(vg, 5,5, &fps);
+
+ nvgEndFrame(vg);
+
+ if (screenshot) {
+ screenshot = 0;
+ saveScreenShot(fbWidth, fbHeight, premult, "dump.png");
+ }
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ freeDemoData(vg, &data);
+
+ nvgDeleteGL2(vg);
+
+ glfwTerminate();
+ return 0;
+}
diff --git a/subprojects/d2tk/nanovg/example/example_gl3.c b/subprojects/d2tk/nanovg/example/example_gl3.c
new file mode 100644
index 0000000..409a145
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/example_gl3.c
@@ -0,0 +1,198 @@
+//
+// Copyright (c) 2013 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <stdio.h>
+#ifdef NANOVG_GLEW
+# include <GL/glew.h>
+#endif
+#ifdef __APPLE__
+# define GLFW_INCLUDE_GLCOREARB
+#endif
+#define GLFW_INCLUDE_GLEXT
+#include <GLFW/glfw3.h>
+#include "nanovg.h"
+#define NANOVG_GL3_IMPLEMENTATION
+#include "nanovg_gl.h"
+#include "demo.h"
+#include "perf.h"
+
+
+void errorcb(int error, const char* desc)
+{
+ printf("GLFW error %d: %s\n", error, desc);
+}
+
+int blowup = 0;
+int screenshot = 0;
+int premult = 0;
+
+static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ NVG_NOTUSED(scancode);
+ NVG_NOTUSED(mods);
+ if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
+ glfwSetWindowShouldClose(window, GL_TRUE);
+ if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
+ blowup = !blowup;
+ if (key == GLFW_KEY_S && action == GLFW_PRESS)
+ screenshot = 1;
+ if (key == GLFW_KEY_P && action == GLFW_PRESS)
+ premult = !premult;
+}
+
+int main()
+{
+ GLFWwindow* window;
+ DemoData data;
+ NVGcontext* vg = NULL;
+ GPUtimer gpuTimer;
+ PerfGraph fps, cpuGraph, gpuGraph;
+ double prevt = 0, cpuTime = 0;
+
+ if (!glfwInit()) {
+ printf("Failed to init GLFW.");
+ return -1;
+ }
+
+ initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time");
+ initGraph(&cpuGraph, GRAPH_RENDER_MS, "CPU Time");
+ initGraph(&gpuGraph, GRAPH_RENDER_MS, "GPU Time");
+
+ glfwSetErrorCallback(errorcb);
+#ifndef _WIN32 // don't require this on win32, and works with more cards
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+#endif
+ glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1);
+
+#ifdef DEMO_MSAA
+ glfwWindowHint(GLFW_SAMPLES, 4);
+#endif
+ window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL);
+// window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL);
+ if (!window) {
+ glfwTerminate();
+ return -1;
+ }
+
+ glfwSetKeyCallback(window, key);
+
+ glfwMakeContextCurrent(window);
+#ifdef NANOVG_GLEW
+ glewExperimental = GL_TRUE;
+ if(glewInit() != GLEW_OK) {
+ printf("Could not init glew.\n");
+ return -1;
+ }
+ // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here.
+ glGetError();
+#endif
+
+#ifdef DEMO_MSAA
+ vg = nvgCreateGL3(NVG_STENCIL_STROKES | NVG_DEBUG);
+#else
+ vg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG);
+#endif
+ if (vg == NULL) {
+ printf("Could not init nanovg.\n");
+ return -1;
+ }
+
+ if (loadDemoData(vg, &data) == -1)
+ return -1;
+
+ glfwSwapInterval(0);
+
+ initGPUTimer(&gpuTimer);
+
+ glfwSetTime(0);
+ prevt = glfwGetTime();
+
+ while (!glfwWindowShouldClose(window))
+ {
+ double mx, my, t, dt;
+ int winWidth, winHeight;
+ int fbWidth, fbHeight;
+ float pxRatio;
+ float gpuTimes[3];
+ int i, n;
+
+ t = glfwGetTime();
+ dt = t - prevt;
+ prevt = t;
+
+ startGPUTimer(&gpuTimer);
+
+ glfwGetCursorPos(window, &mx, &my);
+ glfwGetWindowSize(window, &winWidth, &winHeight);
+ glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
+ // Calculate pixel ration for hi-dpi devices.
+ pxRatio = (float)fbWidth / (float)winWidth;
+
+ // Update and render
+ glViewport(0, 0, fbWidth, fbHeight);
+ if (premult)
+ glClearColor(0,0,0,0);
+ else
+ glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
+
+ nvgBeginFrame(vg, winWidth, winHeight, pxRatio);
+
+ renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data);
+
+ renderGraph(vg, 5,5, &fps);
+ renderGraph(vg, 5+200+5,5, &cpuGraph);
+ if (gpuTimer.supported)
+ renderGraph(vg, 5+200+5+200+5,5, &gpuGraph);
+
+ nvgEndFrame(vg);
+
+ // Measure the CPU time taken excluding swap buffers (as the swap may wait for GPU)
+ cpuTime = glfwGetTime() - t;
+
+ updateGraph(&fps, dt);
+ updateGraph(&cpuGraph, cpuTime);
+
+ // We may get multiple results.
+ n = stopGPUTimer(&gpuTimer, gpuTimes, 3);
+ for (i = 0; i < n; i++)
+ updateGraph(&gpuGraph, gpuTimes[i]);
+
+ if (screenshot) {
+ screenshot = 0;
+ saveScreenShot(fbWidth, fbHeight, premult, "dump.png");
+ }
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ freeDemoData(vg, &data);
+
+ nvgDeleteGL3(vg);
+
+ printf("Average Frame Time: %.2f ms\n", getGraphAverage(&fps) * 1000.0f);
+ printf(" CPU Time: %.2f ms\n", getGraphAverage(&cpuGraph) * 1000.0f);
+ printf(" GPU Time: %.2f ms\n", getGraphAverage(&gpuGraph) * 1000.0f);
+
+ glfwTerminate();
+ return 0;
+}
diff --git a/subprojects/d2tk/nanovg/example/example_gles2.c b/subprojects/d2tk/nanovg/example/example_gles2.c
new file mode 100644
index 0000000..ed78838
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/example_gles2.c
@@ -0,0 +1,155 @@
+//
+// Copyright (c) 2013 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <stdio.h>
+#define GLFW_INCLUDE_ES2
+#define GLFW_INCLUDE_GLEXT
+#include <GLFW/glfw3.h>
+#include "nanovg.h"
+#define NANOVG_GLES2_IMPLEMENTATION
+#include "nanovg_gl.h"
+#include "nanovg_gl_utils.h"
+#include "demo.h"
+#include "perf.h"
+
+
+void errorcb(int error, const char* desc)
+{
+ printf("GLFW error %d: %s\n", error, desc);
+}
+
+int blowup = 0;
+int screenshot = 0;
+int premult = 0;
+
+static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ NVG_NOTUSED(scancode);
+ NVG_NOTUSED(mods);
+ if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
+ glfwSetWindowShouldClose(window, GL_TRUE);
+ if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
+ blowup = !blowup;
+ if (key == GLFW_KEY_S && action == GLFW_PRESS)
+ screenshot = 1;
+ if (key == GLFW_KEY_P && action == GLFW_PRESS)
+ premult = !premult;
+}
+
+int main()
+{
+ GLFWwindow* window;
+ DemoData data;
+ NVGcontext* vg = NULL;
+ PerfGraph fps;
+ double prevt = 0;
+
+ if (!glfwInit()) {
+ printf("Failed to init GLFW.");
+ return -1;
+ }
+
+ initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time");
+
+ glfwSetErrorCallback(errorcb);
+
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL);
+// window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL);
+ if (!window) {
+ glfwTerminate();
+ return -1;
+ }
+
+ glfwSetKeyCallback(window, key);
+
+ glfwMakeContextCurrent(window);
+
+ vg = nvgCreateGLES2(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG);
+ if (vg == NULL) {
+ printf("Could not init nanovg.\n");
+ return -1;
+ }
+
+ if (loadDemoData(vg, &data) == -1)
+ return -1;
+
+ glfwSwapInterval(0);
+
+ glfwSetTime(0);
+ prevt = glfwGetTime();
+
+ while (!glfwWindowShouldClose(window))
+ {
+ double mx, my, t, dt;
+ int winWidth, winHeight;
+ int fbWidth, fbHeight;
+ float pxRatio;
+
+ t = glfwGetTime();
+ dt = t - prevt;
+ prevt = t;
+ updateGraph(&fps, dt);
+
+ glfwGetCursorPos(window, &mx, &my);
+ glfwGetWindowSize(window, &winWidth, &winHeight);
+ glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
+ // Calculate pixel ration for hi-dpi devices.
+ pxRatio = (float)fbWidth / (float)winWidth;
+
+ // Update and render
+ glViewport(0, 0, fbWidth, fbHeight);
+ if (premult)
+ glClearColor(0,0,0,0);
+ else
+ glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+
+ nvgBeginFrame(vg, winWidth, winHeight, pxRatio);
+
+ renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data);
+ renderGraph(vg, 5,5, &fps);
+
+ nvgEndFrame(vg);
+
+ if (screenshot) {
+ screenshot = 0;
+ saveScreenShot(fbWidth, fbHeight, premult, "dump.png");
+ }
+
+ glEnable(GL_DEPTH_TEST);
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ freeDemoData(vg, &data);
+
+ nvgDeleteGLES2(vg);
+
+ glfwTerminate();
+ return 0;
+}
diff --git a/subprojects/d2tk/nanovg/example/example_gles3.c b/subprojects/d2tk/nanovg/example/example_gles3.c
new file mode 100644
index 0000000..4a6084c
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/example_gles3.c
@@ -0,0 +1,155 @@
+//
+// Copyright (c) 2013 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <stdio.h>
+#define GLFW_INCLUDE_ES3
+#define GLFW_INCLUDE_GLEXT
+#include <GLFW/glfw3.h>
+#include "nanovg.h"
+#define NANOVG_GLES3_IMPLEMENTATION
+#include "nanovg_gl.h"
+#include "nanovg_gl_utils.h"
+#include "demo.h"
+#include "perf.h"
+
+
+void errorcb(int error, const char* desc)
+{
+ printf("GLFW error %d: %s\n", error, desc);
+}
+
+int blowup = 0;
+int screenshot = 0;
+int premult = 0;
+
+static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ NVG_NOTUSED(scancode);
+ NVG_NOTUSED(mods);
+ if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
+ glfwSetWindowShouldClose(window, GL_TRUE);
+ if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
+ blowup = !blowup;
+ if (key == GLFW_KEY_S && action == GLFW_PRESS)
+ screenshot = 1;
+ if (key == GLFW_KEY_P && action == GLFW_PRESS)
+ premult = !premult;
+}
+
+int main()
+{
+ GLFWwindow* window;
+ DemoData data;
+ NVGcontext* vg = NULL;
+ PerfGraph fps;
+ double prevt = 0;
+
+ if (!glfwInit()) {
+ printf("Failed to init GLFW.");
+ return -1;
+ }
+
+ initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time");
+
+ glfwSetErrorCallback(errorcb);
+
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL);
+// window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL);
+ if (!window) {
+ glfwTerminate();
+ return -1;
+ }
+
+ glfwSetKeyCallback(window, key);
+
+ glfwMakeContextCurrent(window);
+
+ vg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG);
+ if (vg == NULL) {
+ printf("Could not init nanovg.\n");
+ return -1;
+ }
+
+ if (loadDemoData(vg, &data) == -1)
+ return -1;
+
+ glfwSwapInterval(0);
+
+ glfwSetTime(0);
+ prevt = glfwGetTime();
+
+ while (!glfwWindowShouldClose(window))
+ {
+ double mx, my, t, dt;
+ int winWidth, winHeight;
+ int fbWidth, fbHeight;
+ float pxRatio;
+
+ t = glfwGetTime();
+ dt = t - prevt;
+ prevt = t;
+ updateGraph(&fps, dt);
+
+ glfwGetCursorPos(window, &mx, &my);
+ glfwGetWindowSize(window, &winWidth, &winHeight);
+ glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
+ // Calculate pixel ration for hi-dpi devices.
+ pxRatio = (float)fbWidth / (float)winWidth;
+
+ // Update and render
+ glViewport(0, 0, fbWidth, fbHeight);
+ if (premult)
+ glClearColor(0,0,0,0);
+ else
+ glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+
+ nvgBeginFrame(vg, winWidth, winHeight, pxRatio);
+
+ renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data);
+ renderGraph(vg, 5,5, &fps);
+
+ nvgEndFrame(vg);
+
+ glEnable(GL_DEPTH_TEST);
+
+ if (screenshot) {
+ screenshot = 0;
+ saveScreenShot(fbWidth, fbHeight, premult, "dump.png");
+ }
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ freeDemoData(vg, &data);
+
+ nvgDeleteGLES3(vg);
+
+ glfwTerminate();
+ return 0;
+}
diff --git a/subprojects/d2tk/nanovg/example/images.txt b/subprojects/d2tk/nanovg/example/images.txt
new file mode 100644
index 0000000..96ad626
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images.txt
@@ -0,0 +1,13 @@
+Image credits
+http://cuteoverload.com/2013/11/05/mom-taxi-xvi-birthday-party/
+http://cuteoverload.com/2013/11/05/benson-hedges-private-eye-in-the-case-of-the-crafty-craftsman/
+http://cuteoverload.com/2013/11/05/no-underwater-ballets/
+http://cuteoverload.com/2013/11/05/every-nose-has-a-story/
+http://cuteoverload.com/2013/11/04/nosevember-nozzle-nose/
+http://cuteoverload.com/2013/11/04/this-just-in-super-strength-cute/
+http://cuteoverload.com/2013/11/03/have-a-bunderful-sunday/
+http://cuteoverload.com/2013/11/02/caturday-sense-a-common-theme-here/
+http://cuteoverload.com/2013/11/01/nosevember-1st-24-hours-of-noses-1148pm-pt/
+http://cuteoverload.com/2013/04/02/there-might-be-something-cuter-than-this/
+http://cuteoverload.com/2013/07/17/snorting-micro-peeg-gets-belleh-rubs-interwebs-explode/
+http://cuteoverload.com/2013/08/07/bark-in-the-park-v3-0/ \ No newline at end of file
diff --git a/subprojects/d2tk/nanovg/example/images/image1.jpg b/subprojects/d2tk/nanovg/example/images/image1.jpg
new file mode 100644
index 0000000..c2ce39b
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image1.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image10.jpg b/subprojects/d2tk/nanovg/example/images/image10.jpg
new file mode 100644
index 0000000..d443fb0
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image10.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image11.jpg b/subprojects/d2tk/nanovg/example/images/image11.jpg
new file mode 100644
index 0000000..7429fe5
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image11.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image12.jpg b/subprojects/d2tk/nanovg/example/images/image12.jpg
new file mode 100644
index 0000000..eb0369d
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image12.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image2.jpg b/subprojects/d2tk/nanovg/example/images/image2.jpg
new file mode 100644
index 0000000..1db15ab
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image2.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image3.jpg b/subprojects/d2tk/nanovg/example/images/image3.jpg
new file mode 100644
index 0000000..884f9f2
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image3.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image4.jpg b/subprojects/d2tk/nanovg/example/images/image4.jpg
new file mode 100644
index 0000000..f6e1039
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image4.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image5.jpg b/subprojects/d2tk/nanovg/example/images/image5.jpg
new file mode 100644
index 0000000..d952d16
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image5.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image6.jpg b/subprojects/d2tk/nanovg/example/images/image6.jpg
new file mode 100644
index 0000000..f098087
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image6.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image7.jpg b/subprojects/d2tk/nanovg/example/images/image7.jpg
new file mode 100644
index 0000000..623b4cb
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image7.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image8.jpg b/subprojects/d2tk/nanovg/example/images/image8.jpg
new file mode 100644
index 0000000..123b6da
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image8.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/images/image9.jpg b/subprojects/d2tk/nanovg/example/images/image9.jpg
new file mode 100644
index 0000000..045fadb
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/images/image9.jpg
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/perf.c b/subprojects/d2tk/nanovg/example/perf.c
new file mode 100644
index 0000000..a74dc3c
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/perf.c
@@ -0,0 +1,186 @@
+#include "perf.h"
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#ifdef NANOVG_GLEW
+# include <GL/glew.h>
+#endif
+#include <GLFW/glfw3.h>
+#include "nanovg.h"
+
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#elif !defined(__MINGW32__)
+#include <iconv.h>
+#endif
+
+// timer query support
+#ifndef GL_ARB_timer_query
+#define GL_TIME_ELAPSED 0x88BF
+//typedef void (APIENTRY *pfnGLGETQUERYOBJECTUI64V)(GLuint id, GLenum pname, GLuint64* params);
+//pfnGLGETQUERYOBJECTUI64V glGetQueryObjectui64v = 0;
+#endif
+
+void initGPUTimer(GPUtimer* timer)
+{
+ memset(timer, 0, sizeof(*timer));
+
+/* timer->supported = glfwExtensionSupported("GL_ARB_timer_query");
+ if (timer->supported) {
+#ifndef GL_ARB_timer_query
+ glGetQueryObjectui64v = (pfnGLGETQUERYOBJECTUI64V)glfwGetProcAddress("glGetQueryObjectui64v");
+ printf("glGetQueryObjectui64v=%p\n", glGetQueryObjectui64v);
+ if (!glGetQueryObjectui64v) {
+ timer->supported = GL_FALSE;
+ return;
+ }
+#endif
+ glGenQueries(GPU_QUERY_COUNT, timer->queries);
+ }*/
+}
+
+void startGPUTimer(GPUtimer* timer)
+{
+ if (!timer->supported)
+ return;
+ glBeginQuery(GL_TIME_ELAPSED, timer->queries[timer->cur % GPU_QUERY_COUNT] );
+ timer->cur++;
+}
+
+int stopGPUTimer(GPUtimer* timer, float* times, int maxTimes)
+{
+ NVG_NOTUSED(times);
+ NVG_NOTUSED(maxTimes);
+ GLint available = 1;
+ int n = 0;
+ if (!timer->supported)
+ return 0;
+
+ glEndQuery(GL_TIME_ELAPSED);
+ while (available && timer->ret <= timer->cur) {
+ // check for results if there are any
+ glGetQueryObjectiv(timer->queries[timer->ret % GPU_QUERY_COUNT], GL_QUERY_RESULT_AVAILABLE, &available);
+ if (available) {
+/* GLuint64 timeElapsed = 0;
+ glGetQueryObjectui64v(timer->queries[timer->ret % GPU_QUERY_COUNT], GL_QUERY_RESULT, &timeElapsed);
+ timer->ret++;
+ if (n < maxTimes) {
+ times[n] = (float)((double)timeElapsed * 1e-9);
+ n++;
+ }*/
+ }
+ }
+ return n;
+}
+
+
+void initGraph(PerfGraph* fps, int style, const char* name)
+{
+ memset(fps, 0, sizeof(PerfGraph));
+ fps->style = style;
+ strncpy(fps->name, name, sizeof(fps->name));
+ fps->name[sizeof(fps->name)-1] = '\0';
+}
+
+void updateGraph(PerfGraph* fps, float frameTime)
+{
+ fps->head = (fps->head+1) % GRAPH_HISTORY_COUNT;
+ fps->values[fps->head] = frameTime;
+}
+
+float getGraphAverage(PerfGraph* fps)
+{
+ int i;
+ float avg = 0;
+ for (i = 0; i < GRAPH_HISTORY_COUNT; i++) {
+ avg += fps->values[i];
+ }
+ return avg / (float)GRAPH_HISTORY_COUNT;
+}
+
+void renderGraph(NVGcontext* vg, float x, float y, PerfGraph* fps)
+{
+ int i;
+ float avg, w, h;
+ char str[64];
+
+ avg = getGraphAverage(fps);
+
+ w = 200;
+ h = 35;
+
+ nvgBeginPath(vg);
+ nvgRect(vg, x,y, w,h);
+ nvgFillColor(vg, nvgRGBA(0,0,0,128));
+ nvgFill(vg);
+
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, x, y+h);
+ if (fps->style == GRAPH_RENDER_FPS) {
+ for (i = 0; i < GRAPH_HISTORY_COUNT; i++) {
+ float v = 1.0f / (0.00001f + fps->values[(fps->head+i) % GRAPH_HISTORY_COUNT]);
+ float vx, vy;
+ if (v > 80.0f) v = 80.0f;
+ vx = x + ((float)i/(GRAPH_HISTORY_COUNT-1)) * w;
+ vy = y + h - ((v / 80.0f) * h);
+ nvgLineTo(vg, vx, vy);
+ }
+ } else if (fps->style == GRAPH_RENDER_PERCENT) {
+ for (i = 0; i < GRAPH_HISTORY_COUNT; i++) {
+ float v = fps->values[(fps->head+i) % GRAPH_HISTORY_COUNT] * 1.0f;
+ float vx, vy;
+ if (v > 100.0f) v = 100.0f;
+ vx = x + ((float)i/(GRAPH_HISTORY_COUNT-1)) * w;
+ vy = y + h - ((v / 100.0f) * h);
+ nvgLineTo(vg, vx, vy);
+ }
+ } else {
+ for (i = 0; i < GRAPH_HISTORY_COUNT; i++) {
+ float v = fps->values[(fps->head+i) % GRAPH_HISTORY_COUNT] * 1000.0f;
+ float vx, vy;
+ if (v > 20.0f) v = 20.0f;
+ vx = x + ((float)i/(GRAPH_HISTORY_COUNT-1)) * w;
+ vy = y + h - ((v / 20.0f) * h);
+ nvgLineTo(vg, vx, vy);
+ }
+ }
+ nvgLineTo(vg, x+w, y+h);
+ nvgFillColor(vg, nvgRGBA(255,192,0,128));
+ nvgFill(vg);
+
+ nvgFontFace(vg, "sans");
+
+ if (fps->name[0] != '\0') {
+ nvgFontSize(vg, 14.0f);
+ nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP);
+ nvgFillColor(vg, nvgRGBA(240,240,240,192));
+ nvgText(vg, x+3,y+1, fps->name, NULL);
+ }
+
+ if (fps->style == GRAPH_RENDER_FPS) {
+ nvgFontSize(vg, 18.0f);
+ nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP);
+ nvgFillColor(vg, nvgRGBA(240,240,240,255));
+ sprintf(str, "%.2f FPS", 1.0f / avg);
+ nvgText(vg, x+w-3,y+1, str, NULL);
+
+ nvgFontSize(vg, 15.0f);
+ nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_BOTTOM);
+ nvgFillColor(vg, nvgRGBA(240,240,240,160));
+ sprintf(str, "%.2f ms", avg * 1000.0f);
+ nvgText(vg, x+w-3,y+h-1, str, NULL);
+ }
+ else if (fps->style == GRAPH_RENDER_PERCENT) {
+ nvgFontSize(vg, 18.0f);
+ nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP);
+ nvgFillColor(vg, nvgRGBA(240,240,240,255));
+ sprintf(str, "%.1f %%", avg * 1.0f);
+ nvgText(vg, x+w-3,y+1, str, NULL);
+ } else {
+ nvgFontSize(vg, 18.0f);
+ nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP);
+ nvgFillColor(vg, nvgRGBA(240,240,240,255));
+ sprintf(str, "%.2f ms", avg * 1000.0f);
+ nvgText(vg, x+w-3,y+1, str, NULL);
+ }
+}
diff --git a/subprojects/d2tk/nanovg/example/perf.h b/subprojects/d2tk/nanovg/example/perf.h
new file mode 100644
index 0000000..3ca67b2
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/perf.h
@@ -0,0 +1,46 @@
+#ifndef PERF_H
+#define PERF_H
+
+#include "nanovg.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum GraphrenderStyle {
+ GRAPH_RENDER_FPS,
+ GRAPH_RENDER_MS,
+ GRAPH_RENDER_PERCENT,
+};
+
+#define GRAPH_HISTORY_COUNT 100
+struct PerfGraph {
+ int style;
+ char name[32];
+ float values[GRAPH_HISTORY_COUNT];
+ int head;
+};
+typedef struct PerfGraph PerfGraph;
+
+void initGraph(PerfGraph* fps, int style, const char* name);
+void updateGraph(PerfGraph* fps, float frameTime);
+void renderGraph(NVGcontext* vg, float x, float y, PerfGraph* fps);
+float getGraphAverage(PerfGraph* fps);
+
+#define GPU_QUERY_COUNT 5
+struct GPUtimer {
+ int supported;
+ int cur, ret;
+ unsigned int queries[GPU_QUERY_COUNT];
+};
+typedef struct GPUtimer GPUtimer;
+
+void initGPUTimer(GPUtimer* timer);
+void startGPUTimer(GPUtimer* timer);
+int stopGPUTimer(GPUtimer* timer, float* times, int maxTimes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PERF_H \ No newline at end of file
diff --git a/subprojects/d2tk/nanovg/example/screenshot-01.png b/subprojects/d2tk/nanovg/example/screenshot-01.png
new file mode 100644
index 0000000..d8febe9
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/screenshot-01.png
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/screenshot-02.png b/subprojects/d2tk/nanovg/example/screenshot-02.png
new file mode 100644
index 0000000..7cfa4bc
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/screenshot-02.png
Binary files differ
diff --git a/subprojects/d2tk/nanovg/example/stb_image_write.h b/subprojects/d2tk/nanovg/example/stb_image_write.h
new file mode 100644
index 0000000..5de3159
--- /dev/null
+++ b/subprojects/d2tk/nanovg/example/stb_image_write.h
@@ -0,0 +1,511 @@
+/* stbiw-0.92 - 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
+
+
+Before including,
+
+ #define STB_IMAGE_WRITE_IMPLEMENTATION
+
+in the file that you want to have the implementation.
+
+
+ABOUT:
+
+ This header file is a library for writing images to C stdio. It could be
+ adapted to write to memory or a general streaming interface; let me know.
+
+ 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
+ or run-time performance.
+
+USAGE:
+
+ There are three functions, one for each image file format:
+
+ 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);
+
+ Each function returns 0 on failure and non-0 on success.
+
+ The functions create an image file defined by the parameters. The image
+ is a rectangle of pixels stored from left-to-right, top-to-bottom.
+ Each pixel contains 'comp' channels of data stored interleaved with 8-bits
+ per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
+ monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
+ The *data pointer points to the first byte of the top-left-most pixel.
+ For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
+ a row of pixels to the first byte of the next row of pixels.
+
+ PNG creates output files with the same number of components as the input.
+ The BMP and TGA formats expand Y to RGB in the file format. BMP does not
+ output alpha.
+
+ PNG supports writing rectangles of data even when the bytes storing rows of
+ data are not consecutive in memory (e.g. sub-rectangles of a larger image),
+ by supplying the stride between the beginning of adjacent rows. The other
+ formats do not. (Thus you cannot write a native-format BMP through the BMP
+ writer, both because it is in BGR order and because it may have padding
+ at the end of the line.)
+*/
+
+#ifndef INCLUDE_STB_IMAGE_WRITE_H
+#define INCLUDE_STB_IMAGE_WRITE_H
+
+#ifdef __cplusplus
+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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif//INCLUDE_STB_IMAGE_WRITE_H
+
+#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+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)
+{
+ 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; }
+ default:
+ assert(0);
+ return;
+ }
+ }
+}
+
+static void write3(FILE *f, 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);
+}
+
+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)
+{
+ unsigned char bg[3] = { 255, 0, 255}, px[3];
+ stbiw_uint32 zero = 0;
+ int i,j,k, j_end;
+
+ if (y <= 0)
+ return;
+
+ if (vdir < 0)
+ j_end = -1, j = y-1;
+ else
+ j_end = y, j = 0;
+
+ 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:
+ case 2: write3(f, d[0],d[0],d[0]);
+ 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);
+ }
+ fwrite(&zero,scanline_pad,1,f);
+ }
+}
+
+static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...)
+{
+ FILE *f;
+ if (y < 0 || x < 0) return 0;
+ f = fopen(filename, "wb");
+ if (f) {
+ va_list v;
+ va_start(v, fmt);
+ writefv(f, fmt, v);
+ va_end(v);
+ write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
+ fclose(f);
+ }
+ return f != NULL;
+}
+
+int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
+{
+ int pad = (-x*3) & 3;
+ return outfile(filename,-1,-1,x,y,comp,(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)
+{
+ int has_alpha = !(comp & 1);
+ return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0,
+ "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha);
+}
+
+// stretchy buffer; stbi__sbpush() == vector<>::push_back() -- stbi__sbcount() == vector<>::size()
+#define stbi__sbraw(a) ((int *) (a) - 2)
+#define stbi__sbm(a) stbi__sbraw(a)[0]
+#define stbi__sbn(a) stbi__sbraw(a)[1]
+
+#define stbi__sbneedgrow(a,n) ((a)==0 || stbi__sbn(a)+n >= stbi__sbm(a))
+#define stbi__sbmaybegrow(a,n) (stbi__sbneedgrow(a,(n)) ? stbi__sbgrow(a,n) : 0)
+#define stbi__sbgrow(a,n) stbi__sbgrowf((void **) &(a), (n), sizeof(*(a)))
+
+#define stbi__sbpush(a, v) (stbi__sbmaybegrow(a,1), (a)[stbi__sbn(a)++] = (v))
+#define stbi__sbcount(a) ((a) ? stbi__sbn(a) : 0)
+#define stbi__sbfree(a) ((a) ? free(stbi__sbraw(a)),0 : 0)
+
+static void *stbi__sbgrowf(void **arr, int increment, int itemsize)
+{
+ int m = *arr ? 2*stbi__sbm(*arr)+increment : increment+1;
+ void *p = realloc(*arr ? stbi__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
+ assert(p);
+ if (p) {
+ if (!*arr) ((int *) p)[1] = 0;
+ *arr = (void *) ((int *) p + 2);
+ stbi__sbm(*arr) = m;
+ }
+ return *arr;
+}
+
+static unsigned char *stbi__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
+{
+ while (*bitcount >= 8) {
+ stbi__sbpush(data, (unsigned char) *bitbuffer);
+ *bitbuffer >>= 8;
+ *bitcount -= 8;
+ }
+ return data;
+}
+
+static int stbi__zlib_bitrev(int code, int codebits)
+{
+ int res=0;
+ while (codebits--) {
+ res = (res << 1) | (code & 1);
+ code >>= 1;
+ }
+ return res;
+}
+
+static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int limit)
+{
+ int i;
+ for (i=0; i < limit && i < 258; ++i)
+ if (a[i] != b[i]) break;
+ return i;
+}
+
+static unsigned int stbi__zhash(unsigned char *data)
+{
+ stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+ return hash;
+}
+
+#define stbi__zlib_flush() (out = stbi__zlib_flushf(out, &bitbuf, &bitcount))
+#define stbi__zlib_add(code,codebits) \
+ (bitbuf |= (code) << bitcount, bitcount += (codebits), stbi__zlib_flush())
+#define stbi__zlib_huffa(b,c) stbi__zlib_add(stbi__zlib_bitrev(b,c),c)
+// default huffman tables
+#define stbi__zlib_huff1(n) stbi__zlib_huffa(0x30 + (n), 8)
+#define stbi__zlib_huff2(n) stbi__zlib_huffa(0x190 + (n)-144, 9)
+#define stbi__zlib_huff3(n) stbi__zlib_huffa(0 + (n)-256,7)
+#define stbi__zlib_huff4(n) stbi__zlib_huffa(0xc0 + (n)-280,8)
+#define stbi__zlib_huff(n) ((n) <= 143 ? stbi__zlib_huff1(n) : (n) <= 255 ? stbi__zlib_huff2(n) : (n) <= 279 ? stbi__zlib_huff3(n) : stbi__zlib_huff4(n))
+#define stbi__zlib_huffb(n) ((n) <= 143 ? stbi__zlib_huff1(n) : stbi__zlib_huff2(n))
+
+#define stbi__ZHASH 16384
+
+unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
+{
+ static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };
+ static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
+ static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };
+ static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
+ unsigned int bitbuf=0;
+ int i,j, bitcount=0;
+ unsigned char *out = NULL;
+ unsigned char **hash_table[stbi__ZHASH]; // 64KB on the stack!
+ if (quality < 5) quality = 5;
+
+ stbi__sbpush(out, 0x78); // DEFLATE 32K window
+ stbi__sbpush(out, 0x5e); // FLEVEL = 1
+ stbi__zlib_add(1,1); // BFINAL = 1
+ stbi__zlib_add(1,2); // BTYPE = 1 -- fixed huffman
+
+ for (i=0; i < stbi__ZHASH; ++i)
+ hash_table[i] = NULL;
+
+ i=0;
+ while (i < data_len-3) {
+ // hash next 3 bytes of data to be compressed
+ int h = stbi__zhash(data+i)&(stbi__ZHASH-1), best=3;
+ unsigned char *bestloc = 0;
+ unsigned char **hlist = hash_table[h];
+ int n = stbi__sbcount(hlist);
+ for (j=0; j < n; ++j) {
+ if (hlist[j]-data > i-32768) { // if entry lies within window
+ int d = stbi__zlib_countm(hlist[j], data+i, data_len-i);
+ if (d >= best) best=d,bestloc=hlist[j];
+ }
+ }
+ // when hash table entry is too long, delete half the entries
+ if (hash_table[h] && stbi__sbn(hash_table[h]) == 2*quality) {
+ memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
+ stbi__sbn(hash_table[h]) = quality;
+ }
+ stbi__sbpush(hash_table[h],data+i);
+
+ if (bestloc) {
+ // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
+ h = stbi__zhash(data+i+1)&(stbi__ZHASH-1);
+ hlist = hash_table[h];
+ n = stbi__sbcount(hlist);
+ for (j=0; j < n; ++j) {
+ if (hlist[j]-data > i-32767) {
+ int e = stbi__zlib_countm(hlist[j], data+i+1, data_len-i-1);
+ if (e > best) { // if next match is better, bail on current match
+ bestloc = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ if (bestloc) {
+ int d = data+i - bestloc; // distance back
+ assert(d <= 32767 && best <= 258);
+ for (j=0; best > lengthc[j+1]-1; ++j);
+ stbi__zlib_huff(j+257);
+ if (lengtheb[j]) stbi__zlib_add(best - lengthc[j], lengtheb[j]);
+ for (j=0; d > distc[j+1]-1; ++j);
+ stbi__zlib_add(stbi__zlib_bitrev(j,5),5);
+ if (disteb[j]) stbi__zlib_add(d - distc[j], disteb[j]);
+ i += best;
+ } else {
+ stbi__zlib_huffb(data[i]);
+ ++i;
+ }
+ }
+ // write out final bytes
+ for (;i < data_len; ++i)
+ stbi__zlib_huffb(data[i]);
+ stbi__zlib_huff(256); // end of block
+ // pad with 0 bits to byte boundary
+ while (bitcount)
+ stbi__zlib_add(0,1);
+
+ for (i=0; i < stbi__ZHASH; ++i)
+ (void) stbi__sbfree(hash_table[i]);
+
+ {
+ // compute adler32 on input
+ unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552;
+ int 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;
+ }
+ stbi__sbpush(out, (unsigned char) (s2 >> 8));
+ stbi__sbpush(out, (unsigned char) s2);
+ stbi__sbpush(out, (unsigned char) (s1 >> 8));
+ stbi__sbpush(out, (unsigned char) s1);
+ }
+ *out_len = stbi__sbn(out);
+ // make returned pointer freeable
+ memmove(stbi__sbraw(out), out, *out_len);
+ return (unsigned char *) stbi__sbraw(out);
+}
+
+unsigned int stbi__crc32(unsigned char *buffer, int len)
+{
+ static unsigned int crc_table[256];
+ 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);
+ for (i=0; i < len; ++i)
+ crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
+ return ~crc;
+}
+
+#define stbi__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 stbi__wp32(data,v) stbi__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
+#define stbi__wptag(data,s) stbi__wpng4(data, s[0],s[1],s[2],s[3])
+
+static void stbi__wpcrc(unsigned char **data, int len)
+{
+ unsigned int crc = stbi__crc32(*data - len - 4, len+4);
+ stbi__wp32(*data, crc);
+}
+
+static unsigned char stbi__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;
+}
+
+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 };
+ unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
+ unsigned char *out,*o, *filt, *zlib;
+ signed char *line_buffer;
+ int i,j,k,p,zlen;
+
+ if (stride_bytes == 0)
+ stride_bytes = x * n;
+
+ filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0;
+ line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; }
+ 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 best = 0, bestval = 0x7fffffff;
+ for (p=0; p < 2; ++p) {
+ for (k= p?best:0; k < 5; ++k) {
+ int type = mymap[k],est=0;
+ unsigned char *z = pixels + stride_bytes*j;
+ for (i=0; i < n; ++i)
+ switch (type) {
+ case 0: line_buffer[i] = z[i]; break;
+ case 1: line_buffer[i] = z[i]; break;
+ case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
+ case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break;
+ case 4: line_buffer[i] = (signed char) (z[i] - stbi__paeth(0,z[i-stride_bytes],0)); break;
+ case 5: line_buffer[i] = z[i]; break;
+ case 6: line_buffer[i] = z[i]; break;
+ }
+ for (i=n; i < x*n; ++i) {
+ switch (type) {
+ case 0: line_buffer[i] = z[i]; break;
+ case 1: line_buffer[i] = z[i] - z[i-n]; break;
+ case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
+ case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break;
+ case 4: line_buffer[i] = z[i] - stbi__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break;
+ case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break;
+ case 6: line_buffer[i] = z[i] - stbi__paeth(z[i-n], 0,0); break;
+ }
+ }
+ if (p) break;
+ for (i=0; i < x*n; ++i)
+ est += abs((signed char) line_buffer[i]);
+ if (est < bestval) { bestval = est; best = k; }
+ }
+ }
+ // when we get here, best contains the filter type, and line_buffer contains the data
+ filt[j*(x*n+1)] = (unsigned char) best;
+ memcpy(filt+j*(x*n+1)+1, line_buffer, x*n);
+ }
+ free(line_buffer);
+ zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory
+ free(filt);
+ if (!zlib) return 0;
+
+ // each tag requires 12 bytes of overhead
+ out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12);
+ if (!out) return 0;
+ *out_len = 8 + 12+13 + 12+zlen + 12;
+
+ o=out;
+ memcpy(o,sig,8); o+= 8;
+ stbi__wp32(o, 13); // header length
+ stbi__wptag(o, "IHDR");
+ stbi__wp32(o, x);
+ stbi__wp32(o, y);
+ *o++ = 8;
+ *o++ = (unsigned char) ctype[n];
+ *o++ = 0;
+ *o++ = 0;
+ *o++ = 0;
+ stbi__wpcrc(&o,13);
+
+ stbi__wp32(o, zlen);
+ stbi__wptag(o, "IDAT");
+ memcpy(o, zlib, zlen); o += zlen; free(zlib);
+ stbi__wpcrc(&o, zlen);
+
+ stbi__wp32(o,0);
+ stbi__wptag(o, "IEND");
+ stbi__wpcrc(&o,0);
+
+ assert(o == out + *out_len);
+
+ return out;
+}
+
+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;
+ f = fopen(filename, "wb");
+ if (!f) { free(png); return 0; }
+ fwrite(png, 1, len, f);
+ fclose(f);
+ free(png);
+ return 1;
+}
+#endif // STB_IMAGE_WRITE_IMPLEMENTATION
+
+/* Revision history
+
+ 0.92 (2010-08-01)
+ casts to unsigned char to fix warnings
+ 0.91 (2010-07-17)
+ first public release
+ 0.90 first internal release
+*/