From b5aaa46ca95186bb87dc8fe0d64cc2eec69c37bc Mon Sep 17 00:00:00 2001 From: Blake Lucas Date: Sun, 22 Mar 2026 22:49:33 -0700 Subject: [PATCH] refactored namespace --- .gitignore | 3 + CMakeLists.txt | 45 +- README.md | 6 +- example/demo.c | 1369 ----------------- example/demo.h | 16 +- example/example_fbo.c | 272 ---- example/example_gl2.c | 162 --- example/example_gl3.c | 198 --- example/example_gles2.c | 155 -- example/example_gles3.c | 155 -- example/perf.c | 186 --- example/perf.h | 14 +- obsolete/nanovg_gl2.h | 2 +- obsolete/nanovg_gl3.h | 2 +- premake4.lua | 32 +- src/nanovg.c | 3061 --------------------------------------- src/nanovg.h | 741 ---------- src/nanovg_gl.h | 1712 ---------------------- src/nanovg_gl_utils.h | 154 -- 19 files changed, 67 insertions(+), 8218 deletions(-) delete mode 100644 example/demo.c delete mode 100644 example/example_fbo.c delete mode 100644 example/example_gl2.c delete mode 100644 example/example_gl3.c delete mode 100644 example/example_gles2.c delete mode 100644 example/example_gles3.c delete mode 100644 example/perf.c delete mode 100644 src/nanovg.c delete mode 100644 src/nanovg.h delete mode 100644 src/nanovg_gl.h delete mode 100644 src/nanovg_gl_utils.h diff --git a/.gitignore b/.gitignore index 70f534e..9f4f655 100644 --- a/.gitignore +++ b/.gitignore @@ -24,5 +24,8 @@ Thumbs.db ## Build dir build/* +## Local prebuilt SDKs (optional; used with -DGLFW_ROOT / -DGLEW_ROOT) +third_party/ + ## xcode specific *xcuserdata* diff --git a/CMakeLists.txt b/CMakeLists.txt index 43d32c5..ac3e28a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,8 +27,8 @@ if(WIN32) endif() set(SRC_DIR "./src") -file(GLOB SRCS ${SRC_DIR}/*.c) -file(GLOB HDRS ${SRC_DIR}/*.h) +file(GLOB SRCS ${SRC_DIR}/*.cpp) +file(GLOB HDRS ${SRC_DIR}/*.h ${SRC_DIR}/*.hpp) install(FILES ${HDRS} DESTINATION include/${PROJECT_NAME}/) @@ -42,21 +42,48 @@ if(APPLE) find_library(iokit_library IOKit) set(LIBS ${cocoa_library} ${opengl_library} ${corevideo_library} ${iokit_library}) elseif(WIN32) - set(LIBS glfw gdi32 winmm user32 GLEW glu32 opengl32 kernel32) - # NANOVG_GLEW - set(DEFS _CRT_SECURE_NO_WARNINGS NOMINMAX NDEBUG _CONSOLE GLEW_STATIC UNICODE _UNICODE) + # Match premake: GLFW is "glfw3.lib"; GLEW static is "glew32s.lib" when GLEW_STATIC is defined. + # Pass -DGLFW_ROOT=... and -DGLEW_ROOT=... to point at prebuilt SDKs (include/ + lib folders). + set(WIN_SYS_LIBS gdi32 winmm user32 glu32 opengl32 kernel32) + if(GLFW_ROOT AND GLEW_ROOT AND EXISTS "${GLFW_ROOT}/include/GLFW/glfw3.h" AND EXISTS "${GLEW_ROOT}/include/GL/glew.h") + if(EXISTS "${GLFW_ROOT}/lib-vc2022/glfw3.lib") + set(GLFW_IMPORT_LIB "${GLFW_ROOT}/lib-vc2022/glfw3.lib") + elseif(EXISTS "${GLFW_ROOT}/lib-vc2019/glfw3.lib") + set(GLFW_IMPORT_LIB "${GLFW_ROOT}/lib-vc2019/glfw3.lib") + else() + message(FATAL_ERROR "GLFW_ROOT is set but no lib-vc2019/lib-vc2022/glfw3.lib found under ${GLFW_ROOT}") + endif() + set(GLEW_IMPORT_LIB "${GLEW_ROOT}/lib/Release/x64/glew32s.lib") + if(NOT EXISTS "${GLEW_IMPORT_LIB}") + message(FATAL_ERROR "GLEW_ROOT is set but ${GLEW_IMPORT_LIB} not found (need x64 GLEW static lib)") + endif() + set(LIBS ${GLFW_IMPORT_LIB} ${GLEW_IMPORT_LIB} ${WIN_SYS_LIBS}) + else() + set(LIBS glfw gdi32 winmm user32 GLEW glu32 opengl32 kernel32) + endif() + set(DEFS _CRT_SECURE_NO_WARNINGS NOMINMAX NDEBUG _CONSOLE GLEW_STATIC UNICODE _UNICODE NANOVG_GLEW) else() set(LIBS glfw GLEW GLU GL Xext Xi Xrandr X11 Xxf86vm Xinerama Xcursor Xdamage dl z m) endif() -set(COMMON_FILES "example/demo.c" "example/perf.c") +set(COMMON_FILES "example/demo.cpp" "example/perf.cpp") set(TARGETS ${PROJECT_NAME}) -foreach(EXAMPLE_PROJ IN ITEMS gles2 gles3 gl2 gl3 fbo) - add_executable(example_${EXAMPLE_PROJ} "example/example_${EXAMPLE_PROJ}.c" ${COMMON_FILES}) +# OpenGL ES examples need GLES headers; typical Windows SDK + GLFW do not provide them. +if(WIN32) + set(NANOVG_EXAMPLE_PROJECTS gl2 gl3 fbo) +else() + set(NANOVG_EXAMPLE_PROJECTS gles2 gles3 gl2 gl3 fbo) +endif() +foreach(EXAMPLE_PROJ IN ITEMS ${NANOVG_EXAMPLE_PROJECTS}) + add_executable(example_${EXAMPLE_PROJ} "example/example_${EXAMPLE_PROJ}.cpp" ${COMMON_FILES}) list(APPEND ${TARGETS} example_${EXAMPLE_PROJ}) target_compile_definitions(example_${EXAMPLE_PROJ} PUBLIC ${DEFS}) target_link_libraries(example_${EXAMPLE_PROJ} ${PROJECT_NAME} ${LIBS}) - target_include_directories(example_${EXAMPLE_PROJ} PUBLIC ${SRC_DIR}) + if(WIN32 AND GLFW_ROOT AND GLEW_ROOT AND EXISTS "${GLFW_ROOT}/include/GLFW/glfw3.h") + target_include_directories(example_${EXAMPLE_PROJ} PUBLIC ${SRC_DIR} "${GLFW_ROOT}/include" "${GLEW_ROOT}/include") + else() + target_include_directories(example_${EXAMPLE_PROJ} PUBLIC ${SRC_DIR}) + endif() endforeach() install(TARGETS ${TARGETS} diff --git a/README.md b/README.md index 7b81336..d71d064 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The NanoVG API is modeled loosely on HTML5 canvas API. If you know canvas, you'r The drawing context is created using platform specific constructor function. If you're using the OpenGL 2.0 back-end the context is created as follows: ```C #define NANOVG_GL2_IMPLEMENTATION // Use GL2 implementation. -#include "nanovg_gl.h" +#include "nanovg_gl.hpp" ... struct NVGcontext* vg = nvgCreateGL2(NVG_ANTIALIAS | NVG_STENCIL_STROKES); ``` @@ -29,7 +29,7 @@ The first parameter defines flags for creating the renderer. - `NVG_ANTIALIAS` means that the renderer adjusts the geometry to include anti-aliasing. If you're using MSAA, you can omit this flags. - `NVG_STENCIL_STROKES` means that the render uses better quality rendering for (overlapping) strokes. The quality is mostly visible on wider strokes. If you want speed, you can omit this flag. -Currently there is an OpenGL back-end for NanoVG: [nanovg_gl.h](/src/nanovg_gl.h) for OpenGL 2.0, OpenGL ES 2.0, OpenGL 3.2 core profile and OpenGL ES 3. The implementation can be chosen using a define as in above example. See the header file and examples for further info. +Currently there is an OpenGL back-end for NanoVG: [nanovg_gl.hpp](/src/nanovg_gl.hpp) for OpenGL 2.0, OpenGL ES 2.0, OpenGL 3.2 core profile and OpenGL ES 3. The implementation can be chosen using a define as in above example. See the header file and examples for further info. *NOTE:* The render target you're rendering to must have stencil buffer. @@ -98,7 +98,7 @@ The data for the whole frame is buffered and flushed in `nvgEndFrame()`. The fol ## API Reference -See the header file [nanovg.h](/src/nanovg.h) for API reference. +See the header file [nanovg.hpp](/src/nanovg.hpp) for API reference. ## Ports diff --git a/example/demo.c b/example/demo.c deleted file mode 100644 index bcd359c..0000000 --- a/example/demo.c +++ /dev/null @@ -1,1369 +0,0 @@ -#include "demo.h" -#include -#include -#include -#ifdef NANOVG_GLEW -# include -#endif -#include -#include "nanovg.h" -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" - - -#ifdef _MSC_VER -#define snprintf _snprintf -#elif !defined(__MINGW32__) -#include -#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, 15.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, 17.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, 17.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, 15.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, 17.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, 15.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, 17.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, 15.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, 33); - 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, 17.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, 17.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 drawFancyText(NVGcontext* vg, float x, float y){ - nvgFontSize(vg, 30.0f); - nvgFontFace(vg, "sans-bold"); - nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); - - nvgFontBlur(vg, 10); - nvgFillColor(vg, nvgRGB(255, 0, 102)); - nvgText(vg, x, y, "Font Blur", NULL); - nvgFontBlur(vg,0); - nvgFillColor(vg, nvgRGB(255,255,255)); - nvgText(vg, x, y, "Font Blur", NULL); - - nvgFontDilate(vg, 2); - nvgFillColor(vg, nvgRGB(255, 0, 102)); - nvgText(vg, x, y+40, "Font Outline", NULL); - nvgFontDilate(vg,0); - nvgFillColor(vg, nvgRGB(255,255,255)); - nvgText(vg, x, y+40, "Font Outline", NULL); - - nvgFontDilate(vg, 3); // Dilate will always be applied before blur - nvgFontBlur(vg, 2); - nvgFillColor(vg, nvgRGB(255, 0, 102)); - nvgText(vg, x, y+80, "Font Blur Outline", NULL); - nvgFontDilate(vg,0); - nvgFontBlur(vg,0); - nvgFillColor(vg, nvgRGB(255,255,255)); - nvgText(vg, x, y+80, "Font Blur Outline", NULL); -} - -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); - - // Render hue label - const float tw = 50; - const float th = 25; - r1 += 0.5f*sqrt(tw*tw+th*th); - nvgBeginPath(vg); - nvgFillColor(vg, nvgRGB(32,32,32)); - ax = cx + r1*cosf(hue*NVG_PI*2); - ay = cy + r1*sinf(hue*NVG_PI*2); - nvgRoundedRect(vg, ax - tw*0.5f, ay -th*0.5f, tw, th,5.0f); - nvgFill(vg); - - nvgSave(vg); - nvgTranslate(vg, ax, ay); - nvgScale(vg, 2.0f, 2.0f); // Check that local transforms work with text - nvgFontSize(vg, 0.5f * th); - nvgTextAlign(vg, NVG_ALIGN_CENTER|NVG_ALIGN_TOP); - nvgFontFace(vg, "sans"); - nvgFillColor(vg, nvgRGB(255,255,255)); - char str[128]; - sprintf(str, "%d%%", (int)(100.0f * (hue + 1.0f)) % 100); - nvgBeginPath(vg); - nvgText(vg, 0.0f, -0.2f * th, str, 0); - nvgFill(vg); - - nvgRestore(vg); - nvgRestore(vg); -} - -void drawStylizedLines(NVGcontext* vg, float x, float y, float w, float h){ - nvgLineJoin(vg, NVG_ROUND); - nvgLineStyle(vg, NVG_LINE_DASHED); - nvgStrokeColor(vg,nvgRGBAf(0.6f,0.6f,1.0f,1.0f)); - nvgStrokeWidth(vg, 5.0f); - nvgBeginPath(vg); - nvgRect(vg, x, y, w, h); - nvgStroke(vg); - nvgLineStyle(vg, NVG_LINE_SOLID); -} - -void drawLines(NVGcontext* vg, float x, float y, float w, float h, float strokeWidth, NVGcolor color, 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, strokeWidth); - nvgStrokeColor(vg, color); - 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; - const char* hoverText = "Hover your mouse over the text to see calculated caret position."; - float gx,gy; - int gutter = 0; - NVG_NOTUSED(height); - - nvgSave(vg); - - nvgFontSize(vg, 15.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, 0))) { - 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 + row->minx, y, row->maxx - row->minx, 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, 12.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, 11.0f); - nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); - nvgTextLineHeight(vg, 1.2f); - - nvgTextBoxBounds(vg, x,y, 150, hoverText, NULL, bounds); - - // Fade the tooltip out when close to it. - gx = clampf(mx, bounds[0], bounds[2]) - mx; - gy = clampf(my, bounds[1], bounds[3]) - my; - a = sqrtf(gx*gx + gy*gy) / 30.0f; - 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, hoverText, 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 drawBezierCurve(NVGcontext* vg, float x0, float y0, float radius, float t){ - - float x1 = x0 + radius*cos(2*NVG_PI*t/5); - float y1 = y0 + radius*sin(2*NVG_PI*t/5); - - float cx0 = x0; - float cy0 = y0 + ((y1 - y0) * 0.75f); - float cx1 = x1; - float cy1 = y0 + ((y1 - y0) * 0.75f); - - nvgBeginPath(vg); - nvgMoveTo(vg, x0, y0); - nvgLineTo(vg, cx0, cy0); - nvgLineTo(vg, cx1, cy1); - nvgLineTo(vg, x1, y1); - nvgStrokeColor(vg,nvgRGBA(200,200,200,255)); - nvgStrokeWidth(vg,2.0f); - nvgStroke(vg); - - nvgLineCap(vg, NVG_ROUND); - nvgStrokeWidth(vg,5); - nvgLineJoin(vg, NVG_ROUND); - - nvgBeginPath(vg); - nvgMoveTo(vg, x0, y0); - nvgBezierTo(vg, cx0, cy0, cx1, cy1, x1, y1); - nvgLineStyle(vg, NVG_LINE_SOLID); - nvgStrokeColor(vg, nvgRGBA(40, 53, 147,255)); - nvgStroke(vg); - - nvgLineStyle(vg, NVG_LINE_DASHED); - nvgStrokeColor(vg, nvgRGBA(255, 195, 0,255)); - nvgStroke(vg); - - nvgBeginPath(vg); - nvgCircle(vg,x0,y0,5.0f); - nvgCircle(vg,cx0,cy0,5.0f); - nvgCircle(vg,cx1,cy1,5.0f); - nvgCircle(vg,x1,y1,5.0f); - nvgLineStyle(vg, NVG_LINE_SOLID); - nvgFillColor(vg,nvgRGBA(64,192,64,255)); - nvgFill(vg); -} - -void drawScaledText(NVGcontext* vg, float x0, float y0, float t){ - nvgSave(vg); - const float scale = (cos(2 * NVG_PI * t * 0.25)+1.0) + 0.1; - nvgTranslate(vg, x0, y0); -; nvgScale(vg, scale, scale); - nvgFontSize(vg, 24.0f); - nvgFontFace(vg, "sans-bold"); - nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); - nvgFillColor(vg, nvgRGBA(255,255,255,255)); - nvgBeginPath(vg); - nvgText(vg, 0.0f, 0.0f, "NanoVG", NULL); - 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 - 230, 30, 150, 100, mx, my, t); - drawStylizedLines(vg, width - 245, 15, 180 ,130); - drawFancyText(vg, width - 160, 170); - drawParagraph(vg, width - 450, 50, 150, 100, mx, my); - drawGraph(vg, 0, height/2, width, height/2, t); - drawColorwheel(vg, width - 280, height - 320, 250.0f, 250.0f, t); - - // Line joints - - switch((int)(t/5.0)%3){ - case 0: - nvgLineStyle(vg, NVG_LINE_DASHED);break; - case 1: - nvgLineStyle(vg, NVG_LINE_DOTTED);break; - case 2: - nvgLineStyle(vg, NVG_LINE_GLOW);break; - default: - nvgLineStyle(vg, NVG_LINE_SOLID); - } - drawLines(vg, 100, height-5, 800, 100, 10.0f, nvgRGBA(255, 153, 0, 255), t*3); - - nvgLineStyle(vg, NVG_LINE_SOLID); - drawLines(vg, 120, height-75, 600, 50, 17.0f, nvgRGBA(0,0,0,160), 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); - - drawBezierCurve(vg, width - 380, height - 220, 100, t); - drawScaledText(vg, 450.0, 50, t); -} - -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/example/demo.h b/example/demo.h index aace449..102d050 100644 --- a/example/demo.h +++ b/example/demo.h @@ -1,11 +1,7 @@ #ifndef DEMO_H #define DEMO_H -#include "nanovg.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "nanovg.hpp" struct DemoData { int fontNormal, fontBold, fontIcons, fontEmoji; @@ -13,14 +9,10 @@ struct DemoData { }; 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); +int loadDemoData(nvg::NVGcontext* vg, DemoData* data); +void freeDemoData(nvg::NVGcontext* vg, DemoData* data); +void renderDemo(nvg::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/example/example_fbo.c b/example/example_fbo.c deleted file mode 100644 index cff4ed2..0000000 --- a/example/example_fbo.c +++ /dev/null @@ -1,272 +0,0 @@ -// -// 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 -#ifdef NANOVG_GLEW -# include -#endif -#ifdef __APPLE__ -# define GLFW_INCLUDE_GLCOREARB -#endif -#include -#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/example/example_gl2.c b/example/example_gl2.c deleted file mode 100644 index 7fd5621..0000000 --- a/example/example_gl2.c +++ /dev/null @@ -1,162 +0,0 @@ -// -// 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 -#ifdef NANOVG_GLEW -# include -#endif -#define GLFW_INCLUDE_GLEXT -#include -#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/example/example_gl3.c b/example/example_gl3.c deleted file mode 100644 index 409a145..0000000 --- a/example/example_gl3.c +++ /dev/null @@ -1,198 +0,0 @@ -// -// 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 -#ifdef NANOVG_GLEW -# include -#endif -#ifdef __APPLE__ -# define GLFW_INCLUDE_GLCOREARB -#endif -#define GLFW_INCLUDE_GLEXT -#include -#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/example/example_gles2.c b/example/example_gles2.c deleted file mode 100644 index ed78838..0000000 --- a/example/example_gles2.c +++ /dev/null @@ -1,155 +0,0 @@ -// -// 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 -#define GLFW_INCLUDE_ES2 -#define GLFW_INCLUDE_GLEXT -#include -#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/example/example_gles3.c b/example/example_gles3.c deleted file mode 100644 index 4a6084c..0000000 --- a/example/example_gles3.c +++ /dev/null @@ -1,155 +0,0 @@ -// -// 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 -#define GLFW_INCLUDE_ES3 -#define GLFW_INCLUDE_GLEXT -#include -#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/example/perf.c b/example/perf.c deleted file mode 100644 index e30c552..0000000 --- a/example/perf.c +++ /dev/null @@ -1,186 +0,0 @@ -#include "perf.h" -#include -#include -#include -#ifdef NANOVG_GLEW -# include -#endif -#include -#include "nanovg.h" - -#ifdef _MSC_VER -#define snprintf _snprintf -#elif !defined(__MINGW32__) -#include -#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, 12.0f); - nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); - nvgFillColor(vg, nvgRGBA(240,240,240,192)); - nvgText(vg, x+3,y+3, fps->name, NULL); - } - - if (fps->style == GRAPH_RENDER_FPS) { - nvgFontSize(vg, 15.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+3, str, NULL); - - nvgFontSize(vg, 13.0f); - nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_BASELINE); - nvgFillColor(vg, nvgRGBA(240,240,240,160)); - sprintf(str, "%.2f ms", avg * 1000.0f); - nvgText(vg, x+w-3,y+h-3, str, NULL); - } - else if (fps->style == GRAPH_RENDER_PERCENT) { - nvgFontSize(vg, 15.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+3, str, NULL); - } else { - nvgFontSize(vg, 15.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+3, str, NULL); - } -} diff --git a/example/perf.h b/example/perf.h index 3ca67b2..d18db40 100644 --- a/example/perf.h +++ b/example/perf.h @@ -1,11 +1,7 @@ #ifndef PERF_H #define PERF_H -#include "nanovg.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "nanovg.hpp" enum GraphrenderStyle { GRAPH_RENDER_FPS, @@ -24,7 +20,7 @@ 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); +void renderGraph(nvg::NVGcontext* vg, float x, float y, PerfGraph* fps); float getGraphAverage(PerfGraph* fps); #define GPU_QUERY_COUNT 5 @@ -39,8 +35,4 @@ 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 +#endif // PERF_H diff --git a/obsolete/nanovg_gl2.h b/obsolete/nanovg_gl2.h index ad8883a..f8d0364 100644 --- a/obsolete/nanovg_gl2.h +++ b/obsolete/nanovg_gl2.h @@ -58,7 +58,7 @@ void nvgDeleteGL2(struct NVGcontext* ctx); #include #include #include -#include "nanovg.h" +#include "nanovg.hpp" enum GLNVGuniformLoc { GLNVG_LOC_VIEWSIZE, diff --git a/obsolete/nanovg_gl3.h b/obsolete/nanovg_gl3.h index 971fb6b..88d63e4 100644 --- a/obsolete/nanovg_gl3.h +++ b/obsolete/nanovg_gl3.h @@ -57,7 +57,7 @@ void nvgDeleteGL3(struct NVGcontext* ctx); #include #include #include -#include "nanovg.h" +#include "nanovg.hpp" enum GLNVGuniformLoc { GLNVG_LOC_VIEWSIZE, diff --git a/premake4.lua b/premake4.lua index 0f86168..10f2a26 100644 --- a/premake4.lua +++ b/premake4.lua @@ -7,10 +7,10 @@ solution "nanovg" platforms {"native", "x64", "x32"} project "nanovg" - language "C" + language "C++" kind "StaticLib" includedirs { "src" } - files { "src/*.c" } + files { "src/*.cpp" } targetdir("build") defines { "_CRT_SECURE_NO_WARNINGS" } --,"FONS_USE_FREETYPE" } Uncomment to compile with FreeType support @@ -25,8 +25,8 @@ solution "nanovg" project "example_gl2" kind "ConsoleApp" - language "C" - files { "example/example_gl2.c", "example/demo.c", "example/perf.c" } + language "C++" + files { "example/example_gl2.cpp", "example/demo.cpp", "example/perf.cpp" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -54,8 +54,8 @@ solution "nanovg" project "example_gl3" kind "ConsoleApp" - language "C" - files { "example/example_gl3.c", "example/demo.c", "example/perf.c" } + language "C++" + files { "example/example_gl3.cpp", "example/demo.cpp", "example/perf.cpp" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -83,9 +83,9 @@ solution "nanovg" project "example_gl2_msaa" kind "ConsoleApp" - language "C" + language "C++" defines { "DEMO_MSAA" } - files { "example/example_gl2.c", "example/demo.c", "example/perf.c" } + files { "example/example_gl2.cpp", "example/demo.cpp", "example/perf.cpp" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -113,9 +113,9 @@ solution "nanovg" project "example_gl3_msaa" kind "ConsoleApp" - language "C" + language "C++" defines { "DEMO_MSAA" } - files { "example/example_gl3.c", "example/demo.c", "example/perf.c" } + files { "example/example_gl3.cpp", "example/demo.cpp", "example/perf.cpp" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -143,8 +143,8 @@ solution "nanovg" project "example_fbo" kind "ConsoleApp" - language "C" - files { "example/example_fbo.c", "example/perf.c" } + language "C++" + files { "example/example_fbo.cpp", "example/perf.cpp" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -171,8 +171,8 @@ solution "nanovg" project "example_gles2" kind "ConsoleApp" - language "C" - files { "example/example_gles2.c", "example/demo.c", "example/perf.c" } + language "C++" + files { "example/example_gles2.cpp", "example/demo.cpp", "example/perf.cpp" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -199,8 +199,8 @@ solution "nanovg" project "example_gles3" kind "ConsoleApp" - language "C" - files { "example/example_gles3.c", "example/demo.c", "example/perf.c" } + language "C++" + files { "example/example_gles3.cpp", "example/demo.cpp", "example/perf.cpp" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } diff --git a/src/nanovg.c b/src/nanovg.c deleted file mode 100644 index 6dddf66..0000000 --- a/src/nanovg.c +++ /dev/null @@ -1,3061 +0,0 @@ -// -// 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 -#include -#include -#include - -#include "nanovg.h" -#define FONTSTASH_IMPLEMENTATION -#include "fontstash.h" - -#ifndef NVG_NO_STB -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" -#endif - -#ifdef _MSC_VER -#pragma warning(disable: 4100) // unreferenced formal parameter -#pragma warning(disable: 4127) // conditional expression is constant -#pragma warning(disable: 4204) // nonstandard extension used : non-constant aggregate initializer -#pragma warning(disable: 4706) // assignment within conditional expression -#endif - -#define NVG_INIT_FONTIMAGE_SIZE 512 -#define NVG_MAX_FONTIMAGE_SIZE 2048 -#define NVG_MAX_FONTIMAGES 4 - -#define NVG_INIT_COMMANDS_SIZE 256 -#define NVG_INIT_POINTS_SIZE 128 -#define NVG_INIT_PATHS_SIZE 16 -#define NVG_INIT_VERTS_SIZE 256 - -#ifndef NVG_MAX_STATES -#define NVG_MAX_STATES 32 -#endif - -#define NVG_KAPPA90 0.5522847493f // Length proportional to radius of a cubic bezier handle for 90deg arcs. - -#define NVG_COUNTOF(arr) (sizeof(arr) / sizeof(0[arr])) - - -enum NVGcommands { - NVG_MOVETO = 0, - NVG_LINETO = 1, - NVG_BEZIERTO = 2, - NVG_CLOSE = 3, - NVG_WINDING = 4, -}; - -enum NVGpointFlags -{ - NVG_PT_CORNER = 0x01, - NVG_PT_LEFT = 0x02, - NVG_PT_BEVEL = 0x04, - NVG_PR_INNERBEVEL = 0x08, -}; - -struct NVGstate { - NVGcompositeOperationState compositeOperation; - int shapeAntiAlias; - NVGpaint fill; - NVGpaint stroke; - float strokeWidth; - float miterLimit; - int lineJoin; - int lineCap; - int lineStyle; - float alpha; - float xform[6]; - NVGscissor scissor; - float fontSize; - float letterSpacing; - float lineHeight; - float fontBlur; - float fontDilate; - int textAlign; - int fontId; -}; -typedef struct NVGstate NVGstate; - -struct NVGpoint { - float x,y; - float dx, dy; - float len; - float dmx, dmy; - unsigned char flags; -}; -typedef struct NVGpoint NVGpoint; - -struct NVGpathCache { - NVGpoint* points; - int npoints; - int cpoints; - NVGpath* paths; - int npaths; - int cpaths; - NVGvertex* verts; - int nverts; - int cverts; - float bounds[4]; -}; -typedef struct NVGpathCache NVGpathCache; - -struct NVGcontext { - NVGparams params; - float* commands; - int ccommands; - int ncommands; - float commandx, commandy; - NVGstate states[NVG_MAX_STATES]; - int nstates; - NVGpathCache* cache; - float tessTol; - float distTol; - float fringeWidth; - float devicePxRatio; - struct FONScontext* fs; - int fontImages[NVG_MAX_FONTIMAGES]; - int fontImageIdx; - int drawCallCount; - int fillTriCount; - int strokeTriCount; - int textTriCount; - struct NVGscissorBounds scissor; -}; - -static float nvg__sqrtf(float a) { return sqrtf(a); } -static float nvg__modf(float a, float b) { return fmodf(a, b); } -static float nvg__sinf(float a) { return sinf(a); } -static float nvg__cosf(float a) { return cosf(a); } -static float nvg__tanf(float a) { return tanf(a); } -static float nvg__atan2f(float a,float b) { return atan2f(a, b); } -static float nvg__acosf(float a) { return acosf(a); } - -static int nvg__mini(int a, int b) { return a < b ? a : b; } -static int nvg__maxi(int a, int b) { return a > b ? a : b; } -static int nvg__clampi(int a, int mn, int mx) { return a < mn ? mn : (a > mx ? mx : a); } -static float nvg__minf(float a, float b) { return a < b ? a : b; } -static float nvg__maxf(float a, float b) { return a > b ? a : b; } -static float nvg__absf(float a) { return a >= 0.0f ? a : -a; } -static float nvg__signf(float a) { return a >= 0.0f ? 1.0f : -1.0f; } -static float nvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } -static float nvg__cross(float dx0, float dy0, float dx1, float dy1) { return dx1*dy0 - dx0*dy1; } - -static float nvg__normalize(float *x, float* y) -{ - float d = nvg__sqrtf((*x)*(*x) + (*y)*(*y)); - if (d > 1e-6f) { - float id = 1.0f / d; - *x *= id; - *y *= id; - } - return d; -} - - -static void nvg__deletePathCache(NVGpathCache* c) -{ - if (c == NULL) return; - if (c->points != NULL) free(c->points); - if (c->paths != NULL) free(c->paths); - if (c->verts != NULL) free(c->verts); - free(c); -} - -static NVGpathCache* nvg__allocPathCache(void) -{ - NVGpathCache* c = (NVGpathCache*)malloc(sizeof(NVGpathCache)); - if (c == NULL) goto error; - memset(c, 0, sizeof(NVGpathCache)); - - c->points = (NVGpoint*)malloc(sizeof(NVGpoint)*NVG_INIT_POINTS_SIZE); - if (!c->points) goto error; - c->npoints = 0; - c->cpoints = NVG_INIT_POINTS_SIZE; - - c->paths = (NVGpath*)malloc(sizeof(NVGpath)*NVG_INIT_PATHS_SIZE); - if (!c->paths) goto error; - c->npaths = 0; - c->cpaths = NVG_INIT_PATHS_SIZE; - - c->verts = (NVGvertex*)malloc(sizeof(NVGvertex)*NVG_INIT_VERTS_SIZE); - if (!c->verts) goto error; - c->nverts = 0; - c->cverts = NVG_INIT_VERTS_SIZE; - - return c; -error: - nvg__deletePathCache(c); - return NULL; -} - -static void nvg__setDevicePixelRatio(NVGcontext* ctx, float ratio) -{ - ctx->tessTol = 0.25f / ratio; - ctx->distTol = 0.01f / ratio; - ctx->fringeWidth = 1.0f / ratio; - ctx->devicePxRatio = ratio; -} - -static NVGcompositeOperationState nvg__compositeOperationState(int op) -{ - int sfactor, dfactor; - - if (op == NVG_SOURCE_OVER) - { - sfactor = NVG_ONE; - dfactor = NVG_ONE_MINUS_SRC_ALPHA; - } - else if (op == NVG_SOURCE_IN) - { - sfactor = NVG_DST_ALPHA; - dfactor = NVG_ZERO; - } - else if (op == NVG_SOURCE_OUT) - { - sfactor = NVG_ONE_MINUS_DST_ALPHA; - dfactor = NVG_ZERO; - } - else if (op == NVG_ATOP) - { - sfactor = NVG_DST_ALPHA; - dfactor = NVG_ONE_MINUS_SRC_ALPHA; - } - else if (op == NVG_DESTINATION_OVER) - { - sfactor = NVG_ONE_MINUS_DST_ALPHA; - dfactor = NVG_ONE; - } - else if (op == NVG_DESTINATION_IN) - { - sfactor = NVG_ZERO; - dfactor = NVG_SRC_ALPHA; - } - else if (op == NVG_DESTINATION_OUT) - { - sfactor = NVG_ZERO; - dfactor = NVG_ONE_MINUS_SRC_ALPHA; - } - else if (op == NVG_DESTINATION_ATOP) - { - sfactor = NVG_ONE_MINUS_DST_ALPHA; - dfactor = NVG_SRC_ALPHA; - } - else if (op == NVG_LIGHTER) - { - sfactor = NVG_ONE; - dfactor = NVG_ONE; - } - else if (op == NVG_COPY) - { - sfactor = NVG_ONE; - dfactor = NVG_ZERO; - } - else if (op == NVG_XOR) - { - sfactor = NVG_ONE_MINUS_DST_ALPHA; - dfactor = NVG_ONE_MINUS_SRC_ALPHA; - } - else - { - sfactor = NVG_ONE; - dfactor = NVG_ZERO; - } - - NVGcompositeOperationState state; - state.srcRGB = sfactor; - state.dstRGB = dfactor; - state.srcAlpha = sfactor; - state.dstAlpha = dfactor; - return state; -} - -static NVGstate* nvg__getState(NVGcontext* ctx) -{ - return &ctx->states[ctx->nstates-1]; -} - -NVGcontext* nvgCreateInternal(NVGparams* params) -{ - FONSparams fontParams; - NVGcontext* ctx = (NVGcontext*)malloc(sizeof(NVGcontext)); - int i; - if (ctx == NULL) goto error; - memset(ctx, 0, sizeof(NVGcontext)); - - ctx->params = *params; - for (i = 0; i < NVG_MAX_FONTIMAGES; i++) - ctx->fontImages[i] = 0; - - ctx->commands = (float*)malloc(sizeof(float)*NVG_INIT_COMMANDS_SIZE); - if (!ctx->commands) goto error; - ctx->ncommands = 0; - ctx->ccommands = NVG_INIT_COMMANDS_SIZE; - - ctx->cache = nvg__allocPathCache(); - if (ctx->cache == NULL) goto error; - - nvgSave(ctx); - nvgReset(ctx); - - nvg__setDevicePixelRatio(ctx, 1.0f); - - if (ctx->params.renderCreate(ctx->params.userPtr) == 0) goto error; - - // Init font rendering - memset(&fontParams, 0, sizeof(fontParams)); - fontParams.width = NVG_INIT_FONTIMAGE_SIZE; - fontParams.height = NVG_INIT_FONTIMAGE_SIZE; - fontParams.flags = FONS_ZERO_TOPLEFT; - fontParams.renderCreate = NULL; - fontParams.renderUpdate = NULL; - fontParams.renderDraw = NULL; - fontParams.renderDelete = NULL; - fontParams.userPtr = NULL; - ctx->fs = fonsCreateInternal(&fontParams); - if (ctx->fs == NULL) goto error; - - // Create font texture - ctx->fontImages[0] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, fontParams.width, fontParams.height, 0, NULL); - if (ctx->fontImages[0] == 0) goto error; - ctx->fontImageIdx = 0; - ctx->scissor = (NVGscissorBounds){0.0f, 0.0f, -1.0f, -1.0f}; - return ctx; - -error: - nvgDeleteInternal(ctx); - return 0; -} - -int nvgGetImageTextureId(NVGcontext* ctx, int handle) -{ - return ctx->params.renderGetImageTextureId(ctx->params.userPtr, handle); -} - -NVGparams* nvgInternalParams(NVGcontext* ctx) -{ - return &ctx->params; -} - -struct NVGscissorBounds nvgCurrentScissor(NVGcontext* ctx) { - return ctx->scissor; -} - -void nvgDeleteInternal(NVGcontext* ctx) -{ - int i; - if (ctx == NULL) return; - if (ctx->commands != NULL) free(ctx->commands); - if (ctx->cache != NULL) nvg__deletePathCache(ctx->cache); - - if (ctx->fs) - fonsDeleteInternal(ctx->fs); - - for (i = 0; i < NVG_MAX_FONTIMAGES; i++) { - if (ctx->fontImages[i] != 0) { - nvgDeleteImage(ctx, ctx->fontImages[i]); - ctx->fontImages[i] = 0; - } - } - - if (ctx->params.renderDelete != NULL) - ctx->params.renderDelete(ctx->params.userPtr); - - free(ctx); -} - -void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio) -{ -/* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n", - ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount, - ctx->fillTriCount+ctx->strokeTriCount+ctx->textTriCount);*/ - - ctx->nstates = 0; - nvgSave(ctx); - nvgReset(ctx); - - nvg__setDevicePixelRatio(ctx, devicePixelRatio); - - ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight, devicePixelRatio); - - ctx->drawCallCount = 0; - ctx->fillTriCount = 0; - ctx->strokeTriCount = 0; - ctx->textTriCount = 0; -} - -void nvgCancelFrame(NVGcontext* ctx) -{ - ctx->params.renderCancel(ctx->params.userPtr); -} - -void nvgEndFrame(NVGcontext* ctx) -{ - ctx->params.renderFlush(ctx->params.userPtr); - if (ctx->fontImageIdx != 0) { - int fontImage = ctx->fontImages[ctx->fontImageIdx]; - ctx->fontImages[ctx->fontImageIdx] = 0; - int i, j, iw, ih; - // delete images that smaller than current one - if (fontImage == 0) - return; - nvgImageSize(ctx, fontImage, &iw, &ih); - for (i = j = 0; i < ctx->fontImageIdx; i++) { - if (ctx->fontImages[i] != 0) { - int nw, nh; - int image = ctx->fontImages[i]; - ctx->fontImages[i] = 0; - nvgImageSize(ctx, image, &nw, &nh); - if (nw < iw || nh < ih) - nvgDeleteImage(ctx, image); - else - ctx->fontImages[j++] = image; - } - } - // make current font image to first - ctx->fontImages[j] = ctx->fontImages[0]; - ctx->fontImages[0] = fontImage; - ctx->fontImageIdx = 0; - } -} - -NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b) -{ - return nvgRGBA(r,g,b,255); -} - -NVGcolor nvgRGBf(float r, float g, float b) -{ - return nvgRGBAf(r,g,b,1.0f); -} - -NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - NVGcolor color; - // Use longer initialization to suppress warning. - color.r = r / 255.0f; - color.g = g / 255.0f; - color.b = b / 255.0f; - color.a = a / 255.0f; - return color; -} - -NVGcolor nvgRGBAf(float r, float g, float b, float a) -{ - NVGcolor color; - // Use longer initialization to suppress warning. - color.r = r; - color.g = g; - color.b = b; - color.a = a; - return color; -} - -NVGcolor nvgTransRGBA(NVGcolor c, unsigned char a) -{ - c.a = a / 255.0f; - return c; -} - -NVGcolor nvgTransRGBAf(NVGcolor c, float a) -{ - c.a = a; - return c; -} - -NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u) -{ - int i; - float oneminu; - NVGcolor cint = {{{0}}}; - - u = nvg__clampf(u, 0.0f, 1.0f); - oneminu = 1.0f - u; - for( i = 0; i <4; i++ ) - { - cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u; - } - - return cint; -} - -NVGcolor nvgHSL(float h, float s, float l) -{ - return nvgHSLA(h,s,l,255); -} - -static float nvg__hue(float h, float m1, float m2) -{ - if (h < 0) h += 1; - if (h > 1) h -= 1; - if (h < 1.0f/6.0f) - return m1 + (m2 - m1) * h * 6.0f; - else if (h < 3.0f/6.0f) - return m2; - else if (h < 4.0f/6.0f) - return m1 + (m2 - m1) * (2.0f/3.0f - h) * 6.0f; - return m1; -} - -NVGcolor nvgHSLA(float h, float s, float l, unsigned char a) -{ - float m1, m2; - NVGcolor col; - h = nvg__modf(h, 1.0f); - if (h < 0.0f) h += 1.0f; - s = nvg__clampf(s, 0.0f, 1.0f); - l = nvg__clampf(l, 0.0f, 1.0f); - m2 = l <= 0.5f ? (l * (1 + s)) : (l + s - l * s); - m1 = 2 * l - m2; - col.r = nvg__clampf(nvg__hue(h + 1.0f/3.0f, m1, m2), 0.0f, 1.0f); - col.g = nvg__clampf(nvg__hue(h, m1, m2), 0.0f, 1.0f); - col.b = nvg__clampf(nvg__hue(h - 1.0f/3.0f, m1, m2), 0.0f, 1.0f); - col.a = a/255.0f; - return col; -} - -void nvgTransformIdentity(float* t) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = 0.0f; t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformTranslate(float* t, float tx, float ty) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = 0.0f; t[3] = 1.0f; - t[4] = tx; t[5] = ty; -} - -void nvgTransformScale(float* t, float sx, float sy) -{ - t[0] = sx; t[1] = 0.0f; - t[2] = 0.0f; t[3] = sy; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformRotate(float* t, float a) -{ - float cs = nvg__cosf(a), sn = nvg__sinf(a); - t[0] = cs; t[1] = sn; - t[2] = -sn; t[3] = cs; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformSkewX(float* t, float a) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = nvg__tanf(a); t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformSkewY(float* t, float a) -{ - t[0] = 1.0f; t[1] = nvg__tanf(a); - t[2] = 0.0f; t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformMultiply(float* t, const float* s) -{ - float t0 = t[0] * s[0] + t[1] * s[2]; - float t2 = t[2] * s[0] + t[3] * s[2]; - float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; - t[1] = t[0] * s[1] + t[1] * s[3]; - t[3] = t[2] * s[1] + t[3] * s[3]; - t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; - t[0] = t0; - t[2] = t2; - t[4] = t4; -} - -void nvgTransformPremultiply(float* t, const float* s) -{ - float s2[6]; - memcpy(s2, s, sizeof(float)*6); - nvgTransformMultiply(s2, t); - memcpy(t, s2, sizeof(float)*6); -} - -int nvgTransformInverse(float* inv, const float* t) -{ - double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; - if (det > -1e-6 && det < 1e-6) { - nvgTransformIdentity(inv); - return 0; - } - invdet = 1.0 / det; - inv[0] = (float)(t[3] * invdet); - inv[2] = (float)(-t[2] * invdet); - inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); - inv[1] = (float)(-t[1] * invdet); - inv[3] = (float)(t[0] * invdet); - inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); - return 1; -} - -void nvgTransformPoint(float* dx, float* dy, const float* t, float sx, float sy) -{ - *dx = sx*t[0] + sy*t[2] + t[4]; - *dy = sx*t[1] + sy*t[3] + t[5]; -} - -float nvgDegToRad(float deg) -{ - return deg / 180.0f * NVG_PI; -} - -float nvgRadToDeg(float rad) -{ - return rad / NVG_PI * 180.0f; -} - -static void nvg__setPaintColor(NVGpaint* p, NVGcolor color) -{ - memset(p, 0, sizeof(*p)); - nvgTransformIdentity(p->xform); - p->radius = 0.0f; - p->feather = 1.0f; - p->innerColor = color; - p->outerColor = color; -} - - -// State handling -void nvgSave(NVGcontext* ctx) -{ - if (ctx->nstates >= NVG_MAX_STATES) - return; - if (ctx->nstates > 0) - memcpy(&ctx->states[ctx->nstates], &ctx->states[ctx->nstates-1], sizeof(NVGstate)); - ctx->nstates++; -} - -void nvgRestore(NVGcontext* ctx) -{ - if (ctx->nstates <= 1) - return; - ctx->nstates--; -} - -void nvgReset(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - memset(state, 0, sizeof(*state)); - - nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255)); - nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255)); - state->compositeOperation = nvg__compositeOperationState(NVG_SOURCE_OVER); - state->shapeAntiAlias = 1; - state->strokeWidth = 1.0f; - state->miterLimit = 10.0f; - state->lineCap = NVG_BUTT; - state->lineJoin = NVG_MITER; - state->lineStyle = NVG_LINE_SOLID; - state->alpha = 1.0f; - nvgTransformIdentity(state->xform); - - state->scissor.extent[0] = -1.0f; - state->scissor.extent[1] = -1.0f; - - state->fontSize = 16.0f; - state->letterSpacing = 0.0f; - state->lineHeight = 1.0f; - state->fontBlur = 0.0f; - state->fontDilate = 0.0f; - state->textAlign = NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE; - state->fontId = 0; -} - -// State setting -void nvgShapeAntiAlias(NVGcontext* ctx, int enabled) -{ - NVGstate* state = nvg__getState(ctx); - state->shapeAntiAlias = enabled; -} - -void nvgStrokeWidth(NVGcontext* ctx, float width) -{ - NVGstate* state = nvg__getState(ctx); - state->strokeWidth = width; -} - -float nvgGetStrokeWidth(NVGcontext* ctx) { - NVGstate* state = nvg__getState(ctx); - return state->strokeWidth; -} - -int nvgGetTextAlign(NVGcontext* ctx) { - NVGstate* state = nvg__getState(ctx); - return state->textAlign; -} - -float nvgGetFontSize(NVGcontext* ctx) { - NVGstate* state = nvg__getState(ctx); - return state->fontSize; -} -int nvgGetFontFaceId(NVGcontext* ctx) { - NVGstate* state = nvg__getState(ctx); - return state->fontId; -} - -void nvgMiterLimit(NVGcontext* ctx, float limit) -{ - NVGstate* state = nvg__getState(ctx); - state->miterLimit = limit; -} - -void nvgLineStyle(NVGcontext* ctx, int lineStyle) { - NVGstate* state = nvg__getState(ctx); - state->lineStyle = lineStyle; -} - -void nvgLineCap(NVGcontext* ctx, int cap) -{ - NVGstate* state = nvg__getState(ctx); - state->lineCap = cap; -} - -void nvgLineJoin(NVGcontext* ctx, int join) -{ - NVGstate* state = nvg__getState(ctx); - state->lineJoin = join; -} - -void nvgGlobalAlpha(NVGcontext* ctx, float alpha) -{ - NVGstate* state = nvg__getState(ctx); - state->alpha = alpha; -} - -void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f) -{ - NVGstate* state = nvg__getState(ctx); - float t[6] = { a, b, c, d, e, f }; - nvgTransformPremultiply(state->xform, t); -} - -void nvgResetTransform(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - nvgTransformIdentity(state->xform); -} - -void nvgTranslate(NVGcontext* ctx, float x, float y) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformTranslate(t, x,y); - nvgTransformPremultiply(state->xform, t); -} - -void nvgRotate(NVGcontext* ctx, float angle) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformRotate(t, angle); - nvgTransformPremultiply(state->xform, t); -} - -void nvgSkewX(NVGcontext* ctx, float angle) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformSkewX(t, angle); - nvgTransformPremultiply(state->xform, t); -} - -void nvgSkewY(NVGcontext* ctx, float angle) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformSkewY(t, angle); - nvgTransformPremultiply(state->xform, t); -} - -void nvgScale(NVGcontext* ctx, float x, float y) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformScale(t, x,y); - nvgTransformPremultiply(state->xform, t); -} - -void nvgCurrentTransform(NVGcontext* ctx, float* xform) -{ - NVGstate* state = nvg__getState(ctx); - if (xform == NULL) return; - memcpy(xform, state->xform, sizeof(float)*6); -} - -void nvgStrokeColor(NVGcontext* ctx, NVGcolor color) -{ - NVGstate* state = nvg__getState(ctx); - nvg__setPaintColor(&state->stroke, color); -} - -void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint) -{ - NVGstate* state = nvg__getState(ctx); - state->stroke = paint; - nvgTransformMultiply(state->stroke.xform, state->xform); -} - -void nvgFillColor(NVGcontext* ctx, NVGcolor color) -{ - NVGstate* state = nvg__getState(ctx); - nvg__setPaintColor(&state->fill, color); -} - -void nvgFillPaint(NVGcontext* ctx, NVGpaint paint) -{ - NVGstate* state = nvg__getState(ctx); - state->fill = paint; - nvgTransformMultiply(state->fill.xform, state->xform); -} - -#ifndef NVG_NO_STB -int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) -{ - int w, h, n, image; - unsigned char* img; - stbi_set_unpremultiply_on_load(1); - stbi_convert_iphone_png_to_rgb(1); - img = stbi_load(filename, &w, &h, &n, 4); - if (img == NULL) { -// printf("Failed to load %s - %s\n", filename, stbi_failure_reason()); - return 0; - } - image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img); - stbi_image_free(img); - return image; -} - -int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata) -{ - int w, h, n, image; - stbi_set_unpremultiply_on_load(1); - stbi_convert_iphone_png_to_rgb(1); - unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); - if (img == NULL) { -// printf("Failed to load %s - %s\n", filename, stbi_failure_reason()); - return 0; - } - image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img); - stbi_image_free(img); - return image; -} -#endif - -int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data) -{ - return ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_RGBA, w, h, imageFlags, data); -} - -void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data) -{ - int w, h; - ctx->params.renderGetTextureSize(ctx->params.userPtr, image, &w, &h); - ctx->params.renderUpdateTexture(ctx->params.userPtr, image, 0,0, w,h, data); -} - -void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h) -{ - ctx->params.renderGetTextureSize(ctx->params.userPtr, image, w, h); -} - -void nvgDeleteImage(NVGcontext* ctx, int image) -{ - ctx->params.renderDeleteTexture(ctx->params.userPtr, image); -} - -NVGpaint nvgLinearGradient(NVGcontext* ctx, - float sx, float sy, float ex, float ey, - NVGcolor icol, NVGcolor ocol) -{ - NVGpaint p; - float dx, dy, d; - const float large = 1e5; - NVG_NOTUSED(ctx); - memset(&p, 0, sizeof(p)); - - // Calculate transform aligned to the line - dx = ex - sx; - dy = ey - sy; - d = sqrtf(dx*dx + dy*dy); - if (d > 0.0001f) { - dx /= d; - dy /= d; - } else { - dx = 0; - dy = 1; - } - - p.xform[0] = dy; p.xform[1] = -dx; - p.xform[2] = dx; p.xform[3] = dy; - p.xform[4] = sx - dx*large; p.xform[5] = sy - dy*large; - - p.extent[0] = large; - p.extent[1] = large + d*0.5f; - - p.radius = 0.0f; - - p.feather = nvg__maxf(1.0f, d); - - p.innerColor = icol; - p.outerColor = ocol; - - return p; -} - -NVGpaint nvgRadialGradient(NVGcontext* ctx, - float cx, float cy, float inr, float outr, - NVGcolor icol, NVGcolor ocol) -{ - NVGpaint p; - float r = (inr+outr)*0.5f; - float f = (outr-inr); - NVG_NOTUSED(ctx); - memset(&p, 0, sizeof(p)); - - nvgTransformIdentity(p.xform); - p.xform[4] = cx; - p.xform[5] = cy; - - p.extent[0] = r; - p.extent[1] = r; - - p.radius = r; - - p.feather = nvg__maxf(1.0f, f); - - p.innerColor = icol; - p.outerColor = ocol; - - return p; -} - -NVGpaint nvgBoxGradient(NVGcontext* ctx, - float x, float y, float w, float h, float r, float f, - NVGcolor icol, NVGcolor ocol) -{ - NVGpaint p; - NVG_NOTUSED(ctx); - memset(&p, 0, sizeof(p)); - - nvgTransformIdentity(p.xform); - p.xform[4] = x+w*0.5f; - p.xform[5] = y+h*0.5f; - - p.extent[0] = w*0.5f; - p.extent[1] = h*0.5f; - - p.radius = r; - - p.feather = nvg__maxf(1.0f, f); - - p.innerColor = icol; - p.outerColor = ocol; - - return p; -} - - -NVGpaint nvgImagePattern(NVGcontext* ctx, - float cx, float cy, float w, float h, float angle, - int image, float alpha) -{ - NVGpaint p; - NVG_NOTUSED(ctx); - memset(&p, 0, sizeof(p)); - - nvgTransformRotate(p.xform, angle); - p.xform[4] = cx; - p.xform[5] = cy; - - p.extent[0] = w; - p.extent[1] = h; - - p.image = image; - - p.innerColor = p.outerColor = nvgRGBAf(1,1,1,alpha); - - return p; -} - -// Scissoring -void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h) -{ - NVGstate* state = nvg__getState(ctx); - ctx->scissor = (NVGscissorBounds){x, y, w, h}; - w = nvg__maxf(0.0f, w); - h = nvg__maxf(0.0f, h); - - nvgTransformIdentity(state->scissor.xform); - state->scissor.xform[4] = x+w*0.5f; - state->scissor.xform[5] = y+h*0.5f; - nvgTransformMultiply(state->scissor.xform, state->xform); - - state->scissor.extent[0] = w*0.5f; - state->scissor.extent[1] = h*0.5f; -} - -static void nvg__isectRects(float* dst, - float ax, float ay, float aw, float ah, - float bx, float by, float bw, float bh) -{ - float minx = nvg__maxf(ax, bx); - float miny = nvg__maxf(ay, by); - float maxx = nvg__minf(ax+aw, bx+bw); - float maxy = nvg__minf(ay+ah, by+bh); - dst[0] = minx; - dst[1] = miny; - dst[2] = nvg__maxf(0.0f, maxx - minx); - dst[3] = nvg__maxf(0.0f, maxy - miny); -} - -void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h) -{ - NVGstate* state = nvg__getState(ctx); - float pxform[6], invxorm[6]; - float rect[4]; - float ex, ey, tex, tey; - - // If no previous scissor has been set, set the scissor as current scissor. - if (state->scissor.extent[0] < 0) { - nvgScissor(ctx, x, y, w, h); - return; - } - - // Transform the current scissor rect into current transform space. - // If there is difference in rotation, this will be approximation. - memcpy(pxform, state->scissor.xform, sizeof(float)*6); - ex = state->scissor.extent[0]; - ey = state->scissor.extent[1]; - nvgTransformInverse(invxorm, state->xform); - nvgTransformMultiply(pxform, invxorm); - tex = ex*nvg__absf(pxform[0]) + ey*nvg__absf(pxform[2]); - tey = ex*nvg__absf(pxform[1]) + ey*nvg__absf(pxform[3]); - - // Intersect rects. - nvg__isectRects(rect, pxform[4]-tex,pxform[5]-tey,tex*2,tey*2, x,y,w,h); - - nvgScissor(ctx, rect[0], rect[1], rect[2], rect[3]); -} - -void nvgResetScissor(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - memset(state->scissor.xform, 0, sizeof(state->scissor.xform)); - state->scissor.extent[0] = -1.0f; - state->scissor.extent[1] = -1.0f; -} - -// Global composite operation. -void nvgGlobalCompositeOperation(NVGcontext* ctx, int op) -{ - NVGstate* state = nvg__getState(ctx); - state->compositeOperation = nvg__compositeOperationState(op); -} - -void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor) -{ - nvgGlobalCompositeBlendFuncSeparate(ctx, sfactor, dfactor, sfactor, dfactor); -} - -void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) -{ - NVGcompositeOperationState op; - op.srcRGB = srcRGB; - op.dstRGB = dstRGB; - op.srcAlpha = srcAlpha; - op.dstAlpha = dstAlpha; - - NVGstate* state = nvg__getState(ctx); - state->compositeOperation = op; -} - -static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol) -{ - float dx = x2 - x1; - float dy = y2 - y1; - return dx*dx + dy*dy < tol*tol; -} - -static float nvg__distPtSeg(float x, float y, float px, float py, float qx, float qy) -{ - float pqx, pqy, dx, dy, d, t; - pqx = qx-px; - pqy = qy-py; - dx = x-px; - dy = y-py; - d = pqx*pqx + pqy*pqy; - t = pqx*dx + pqy*dy; - if (d > 0) t /= d; - if (t < 0) t = 0; - else if (t > 1) t = 1; - dx = px + t*pqx - x; - dy = py + t*pqy - y; - return dx*dx + dy*dy; -} - -static void nvg__appendCommands(NVGcontext* ctx, float* vals, int nvals) -{ - NVGstate* state = nvg__getState(ctx); - int i; - - if (ctx->ncommands+nvals > ctx->ccommands) { - float* commands; - int ccommands = ctx->ncommands+nvals + ctx->ccommands/2; - commands = (float*)realloc(ctx->commands, sizeof(float)*ccommands); - if (commands == NULL) return; - ctx->commands = commands; - ctx->ccommands = ccommands; - } - - if ((int)vals[0] != NVG_CLOSE && (int)vals[0] != NVG_WINDING) { - ctx->commandx = vals[nvals-2]; - ctx->commandy = vals[nvals-1]; - } - - // transform commands - i = 0; - while (i < nvals) { - int cmd = (int)vals[i]; - switch (cmd) { - case NVG_MOVETO: - nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]); - i += 3; - break; - case NVG_LINETO: - nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]); - i += 3; - break; - case NVG_BEZIERTO: - nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]); - nvgTransformPoint(&vals[i+3],&vals[i+4], state->xform, vals[i+3],vals[i+4]); - nvgTransformPoint(&vals[i+5],&vals[i+6], state->xform, vals[i+5],vals[i+6]); - i += 7; - break; - case NVG_CLOSE: - i++; - break; - case NVG_WINDING: - i += 2; - break; - default: - i++; - } - } - - memcpy(&ctx->commands[ctx->ncommands], vals, nvals*sizeof(float)); - - ctx->ncommands += nvals; -} - - -static void nvg__clearPathCache(NVGcontext* ctx) -{ - ctx->cache->npoints = 0; - ctx->cache->npaths = 0; -} - -static NVGpath* nvg__lastPath(NVGcontext* ctx) -{ - if (ctx->cache->npaths > 0) - return &ctx->cache->paths[ctx->cache->npaths-1]; - return NULL; -} - -static void nvg__addPath(NVGcontext* ctx) -{ - NVGpath* path; - if (ctx->cache->npaths+1 > ctx->cache->cpaths) { - NVGpath* paths; - int cpaths = ctx->cache->npaths+1 + ctx->cache->cpaths/2; - paths = (NVGpath*)realloc(ctx->cache->paths, sizeof(NVGpath)*cpaths); - if (paths == NULL) return; - ctx->cache->paths = paths; - ctx->cache->cpaths = cpaths; - } - path = &ctx->cache->paths[ctx->cache->npaths]; - memset(path, 0, sizeof(*path)); - path->first = ctx->cache->npoints; - path->winding = NVG_CCW; - - ctx->cache->npaths++; -} - -static NVGpoint* nvg__lastPoint(NVGcontext* ctx) -{ - if (ctx->cache->npoints > 0) - return &ctx->cache->points[ctx->cache->npoints-1]; - return NULL; -} - -static void nvg__addPoint(NVGcontext* ctx, float x, float y, int flags) -{ - NVGpath* path = nvg__lastPath(ctx); - NVGpoint* pt; - if (path == NULL) return; - - if (path->count > 0 && ctx->cache->npoints > 0) { - pt = nvg__lastPoint(ctx); - if (nvg__ptEquals(pt->x,pt->y, x,y, ctx->distTol)) { - pt->flags |= flags; - return; - } - } - - if (ctx->cache->npoints+1 > ctx->cache->cpoints) { - NVGpoint* points; - int cpoints = ctx->cache->npoints+1 + ctx->cache->cpoints/2; - points = (NVGpoint*)realloc(ctx->cache->points, sizeof(NVGpoint)*cpoints); - if (points == NULL) return; - ctx->cache->points = points; - ctx->cache->cpoints = cpoints; - } - - pt = &ctx->cache->points[ctx->cache->npoints]; - memset(pt, 0, sizeof(*pt)); - pt->x = x; - pt->y = y; - pt->flags = (unsigned char)flags; - - ctx->cache->npoints++; - path->count++; -} - -static void nvg__closePath(NVGcontext* ctx) -{ - NVGpath* path = nvg__lastPath(ctx); - if (path == NULL) return; - path->closed = 1; -} - -static void nvg__pathWinding(NVGcontext* ctx, int winding) -{ - NVGpath* path = nvg__lastPath(ctx); - if (path == NULL) return; - path->winding = winding; -} - -static float nvg__getAverageScale(float *t) -{ - float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); - float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); - return (sx + sy) * 0.5f; -} - -static NVGvertex* nvg__allocTempVerts(NVGcontext* ctx, int nverts) -{ - if (nverts > ctx->cache->cverts) { - NVGvertex* verts; - int cverts = (nverts + 0xff) & ~0xff; // Round up to prevent allocations when things change just slightly. - verts = (NVGvertex*)realloc(ctx->cache->verts, sizeof(NVGvertex)*cverts); - if (verts == NULL) return NULL; - ctx->cache->verts = verts; - ctx->cache->cverts = cverts; - } - - return ctx->cache->verts; -} - -static float nvg__triarea2(float ax, float ay, float bx, float by, float cx, float cy) -{ - float abx = bx - ax; - float aby = by - ay; - float acx = cx - ax; - float acy = cy - ay; - return acx*aby - abx*acy; -} - -static float nvg__polyArea(NVGpoint* pts, int npts) -{ - int i; - float area = 0; - for (i = 2; i < npts; i++) { - NVGpoint* a = &pts[0]; - NVGpoint* b = &pts[i-1]; - NVGpoint* c = &pts[i]; - area += nvg__triarea2(a->x,a->y, b->x,b->y, c->x,c->y); - } - return area * 0.5f; -} - -static void nvg__polyReverse(NVGpoint* pts, int npts) -{ - NVGpoint tmp; - int i = 0, j = npts-1; - while (i < j) { - tmp = pts[i]; - pts[i] = pts[j]; - pts[j] = tmp; - i++; - j--; - } -} - - -static void nvg__vset(NVGvertex* vtx, float x, float y, float u, float v, float s, float t) -{ - vtx->x = x; - vtx->y = y; - vtx->u = u; - vtx->v = v; - vtx->s = s; // Normalized line width [-1, 1] - vtx->t = t; // Path length normalized by stroke width -} - -static void nvg__tesselateBezier(NVGcontext* ctx, - float x1, float y1, float x2, float y2, - float x3, float y3, float x4, float y4, - int level, int type) -{ - float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; - float dx,dy,d2,d3; - - if (level > 10) return; - - x12 = (x1+x2)*0.5f; - y12 = (y1+y2)*0.5f; - x23 = (x2+x3)*0.5f; - y23 = (y2+y3)*0.5f; - x34 = (x3+x4)*0.5f; - y34 = (y3+y4)*0.5f; - x123 = (x12+x23)*0.5f; - y123 = (y12+y23)*0.5f; - - dx = x4 - x1; - dy = y4 - y1; - d2 = nvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); - d3 = nvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); - - if ((d2 + d3)*(d2 + d3) < ctx->tessTol * (dx*dx + dy*dy)) { - nvg__addPoint(ctx, x4, y4, type); - return; - } - -/* if (nvg__absf(x1+x3-x2-x2) + nvg__absf(y1+y3-y2-y2) + nvg__absf(x2+x4-x3-x3) + nvg__absf(y2+y4-y3-y3) < ctx->tessTol) { - nvg__addPoint(ctx, x4, y4, type); - return; - }*/ - - x234 = (x23+x34)*0.5f; - y234 = (y23+y34)*0.5f; - x1234 = (x123+x234)*0.5f; - y1234 = (y123+y234)*0.5f; - - nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); - nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); -} - -static void nvg__flattenPaths(NVGcontext* ctx) -{ - NVGpathCache* cache = ctx->cache; - // NVGstate* state = nvg__getState(ctx); - NVGpoint* last; - NVGpoint* p0; - NVGpoint* p1; - NVGpoint* pts; - NVGpath* path; - int i, j; - float* cp1; - float* cp2; - float* p; - float area; - - if (cache->npaths > 0) - return; - - // Flatten - i = 0; - while (i < ctx->ncommands) { - int cmd = (int)ctx->commands[i]; - switch (cmd) { - case NVG_MOVETO: - nvg__addPath(ctx); - p = &ctx->commands[i+1]; - nvg__addPoint(ctx, p[0], p[1], NVG_PT_CORNER); - i += 3; - break; - case NVG_LINETO: - p = &ctx->commands[i+1]; - nvg__addPoint(ctx, p[0], p[1], NVG_PT_CORNER); - i += 3; - break; - case NVG_BEZIERTO: - last = nvg__lastPoint(ctx); - if (last != NULL) { - cp1 = &ctx->commands[i+1]; - cp2 = &ctx->commands[i+3]; - p = &ctx->commands[i+5]; - nvg__tesselateBezier(ctx, last->x,last->y, cp1[0],cp1[1], cp2[0],cp2[1], p[0],p[1], 0, NVG_PT_CORNER); - } - i += 7; - break; - case NVG_CLOSE: - nvg__closePath(ctx); - i++; - break; - case NVG_WINDING: - nvg__pathWinding(ctx, (int)ctx->commands[i+1]); - i += 2; - break; - default: - i++; - } - } - - cache->bounds[0] = cache->bounds[1] = 1e6f; - cache->bounds[2] = cache->bounds[3] = -1e6f; - - // Calculate the direction and length of line segments. - for (j = 0; j < cache->npaths; j++) { - path = &cache->paths[j]; - pts = &cache->points[path->first]; - - // If the first and last points are the same, remove the last, mark as closed path. - p0 = &pts[path->count-1]; - p1 = &pts[0]; - if (nvg__ptEquals(p0->x,p0->y, p1->x,p1->y, ctx->distTol)) { - path->count--; - p0 = &pts[path->count-1]; - path->closed = 1; - } - - // Enforce winding. - path->reversed = 0; - if (path->count > 2) { - area = nvg__polyArea(pts, path->count); - if (path->winding == NVG_CCW && area < 0.0f) { - nvg__polyReverse(pts, path->count); - path->reversed = 1; - } - if (path->winding == NVG_CW && area > 0.0f) { - nvg__polyReverse(pts, path->count); - path->reversed = 1; - } - } - - for(i = 0; i < path->count; i++) { - // Calculate segment direction and length - p0->dx = p1->x - p0->x; - p0->dy = p1->y - p0->y; - p0->len = nvg__normalize(&p0->dx, &p0->dy); - // Update bounds - cache->bounds[0] = nvg__minf(cache->bounds[0], p0->x); - cache->bounds[1] = nvg__minf(cache->bounds[1], p0->y); - cache->bounds[2] = nvg__maxf(cache->bounds[2], p0->x); - cache->bounds[3] = nvg__maxf(cache->bounds[3], p0->y); - // Advance - p0 = p1++; - } - } -} - -static int nvg__curveDivs(float r, float arc, float tol) -{ - float da = acosf(r / (r + tol)) * 2.0f; - return nvg__maxi(2, (int)ceilf(arc / da)); -} - -static void nvg__chooseBevel(int bevel, NVGpoint* p0, NVGpoint* p1, float w, - float* x0, float* y0, float* x1, float* y1) -{ - if (bevel) { - *x0 = p1->x + p0->dy * w; - *y0 = p1->y - p0->dx * w; - *x1 = p1->x + p1->dy * w; - *y1 = p1->y - p1->dx * w; - } else { - *x0 = p1->x + p1->dmx * w; - *y0 = p1->y + p1->dmy * w; - *x1 = p1->x + p1->dmx * w; - *y1 = p1->y + p1->dmy * w; - } -} - -static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, - float lw, float rw, float lu, float ru, int ncap, - float fringe, float t) -{ - int i, n; - float dlx0 = p0->dy; - float dly0 = -p0->dx; - float dlx1 = p1->dy; - float dly1 = -p1->dx; - NVG_NOTUSED(fringe); - - if (p1->flags & NVG_PT_LEFT) { - float lx0,ly0,lx1,ly1,a0,a1; - nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, lw, &lx0,&ly0, &lx1,&ly1); - a0 = atan2f(-dly0, -dlx0); - a1 = atan2f(-dly1, -dlx1); - if (a1 > a0) a1 -= NVG_PI*2; - - nvg__vset(dst, lx0, ly0, lu, 1, -1, t); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru, 1, 1, t); dst++; - - n = nvg__clampi((int)ceilf(((a0 - a1) / NVG_PI) * ncap), 2, ncap); - for (i = 0; i < n; i++) { - float u = i/(float)(n-1); - float a = a0 + u*(a1-a0); - float rx = p1->x + cosf(a) * rw; - float ry = p1->y + sinf(a) * rw; - nvg__vset(dst, p1->x, p1->y, 0.5f, 1, 0, t); dst++; - nvg__vset(dst, rx, ry, ru,1, 1, t); dst++; - } - - nvg__vset(dst, lx1, ly1, lu, 1, -1, t); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru, 1, 1, t); dst++; - - } else { - float rx0,ry0,rx1,ry1,a0,a1; - nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1); - a0 = atan2f(dly0, dlx0); - a1 = atan2f(dly1, dlx1); - if (a1 < a0) a1 += NVG_PI*2; - - nvg__vset(dst, p1->x + dlx0*rw, p1->y + dly0*rw, lu,1, -1, t);dst++; - nvg__vset(dst, rx0, ry0, ru, 1, 1, t); dst++; - - n = nvg__clampi((int)ceilf(((a1 - a0) / NVG_PI) * ncap), 2, ncap); - for (i = 0; i < n; i++) { - float u = i/(float)(n-1); - float a = a0 + u*(a1-a0); - float lx = p1->x + cosf(a) * lw; - float ly = p1->y + sinf(a) * lw; - nvg__vset(dst, lx, ly, lu, 1, 1, t); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f, 1, 0.0, t); dst++; - } - - nvg__vset(dst, p1->x + dlx1*rw, p1->y + dly1*rw, lu, 1, -1, t); dst++; - nvg__vset(dst, rx1, ry1, ru, 1, 1, t); dst++; - - } - return dst; -} - -static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, - float lw, float rw, float lu, float ru, float fringe, float t) -{ - float rx0,ry0,rx1,ry1; - float lx0,ly0,lx1,ly1; - float dlx0 = p0->dy; - float dly0 = -p0->dx; - float dlx1 = p1->dy; - float dly1 = -p1->dx; - NVG_NOTUSED(fringe); - - if (p1->flags & NVG_PT_LEFT) { - nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, lw, &lx0,&ly0, &lx1,&ly1); - - nvg__vset(dst, lx0, ly0, lu, 1, -1, t); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru, 1, 1, t); dst++; - - if (p1->flags & NVG_PT_BEVEL) { - nvg__vset(dst, lx0, ly0, lu, 1, -1, t); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru, 1, 1, t); dst++; - - nvg__vset(dst, lx1, ly1, lu, 1, -1, t); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru, 1, 1, t); dst++; - } else { - rx0 = p1->x - p1->dmx * rw; - ry0 = p1->y - p1->dmy * rw; - - nvg__vset(dst, p1->x, p1->y, 0.5f, 1, -1, t); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru, 1, 1, t); dst++; - - nvg__vset(dst, rx0, ry0, ru,1, -1, t); dst++; - nvg__vset(dst, rx0, ry0, ru,1, 1, t); dst++; - - nvg__vset(dst, p1->x, p1->y, 0.5f, 1, -1, t); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru, 1, 1, t); dst++; - } - - nvg__vset(dst, lx1, ly1, lu, 1, -1, t); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1, 1, t); dst++; - - } else { - nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1); - - nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu, 1, 1, t); dst++; - nvg__vset(dst, rx0, ry0, ru, 1, -1, t); dst++; - - if (p1->flags & NVG_PT_BEVEL) { - nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu, 1, 1, t); dst++; - nvg__vset(dst, rx0, ry0, ru, 1, -1, t); dst++; - - nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu, 1, 1, t); dst++; - nvg__vset(dst, rx1, ry1, ru, 1, -1, t); dst++; - } else { - lx0 = p1->x + p1->dmx * lw; - ly0 = p1->y + p1->dmy * lw; - - nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu, 1, 1, t); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f,1, -1, t); dst++; - - nvg__vset(dst, lx0, ly0, lu, 1, 1, t); dst++; - nvg__vset(dst, lx0, ly0, lu, 1, -1, t); dst++; - - nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu, 1, 1, t); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f, 1, -1, t); dst++; - } - - nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu, 1, 1, t); dst++; - nvg__vset(dst, rx1, ry1, ru, 1, -1, t); dst++; - } - - return dst; -} - -static NVGvertex* nvg_insertSpacer(NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, float u0, float u1, float t){ - float dlx = dy; - float dly = -dx; - float px = p->x; - float py = p->y; - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1,-1, t); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1, 1, t); dst++; - return dst; -} - -static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p, - float dx, float dy, float w, float d, - float aa, float u0, float u1, float t, int dir) -{ - float px = p->x - dx*d; - float py = p->y - dy*d; - float dlx = dy; - float dly = -dx; - nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, u0,0, -1, t - dir * aa / w); dst++; - nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0, 1, t - dir * aa / w); dst++; - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1, -1, t); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1, 1, t); dst++; - return dst; -} - -static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p, - float dx, float dy, float w, float d, - float aa, float u0, float u1, float t, int dir) -{ - float px = p->x + dx*d; - float py = p->y + dy*d; - float dlx = dy; - float dly = -dx; - nvg__vset(dst, px + dlx*w, py + dly*w, u0, 1 , -1, t); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1, 1 , 1, t); dst++; - nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0, 0, -1, t + dir * aa / w); dst++; - nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1, 0, 1, t + dir * aa / w); dst++; - return dst; -} - - -static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, - float dx, float dy, float w, int ncap, - float aa, float u0, float u1, float t, int dir) -{ - int i; - float px = p->x; - float py = p->y; - float dlx = dy; - float dly = -dx; - NVG_NOTUSED(aa); - for (i = 0; i < ncap; i++) { - const float a = i/(float)(ncap-1)*NVG_PI; - float ax = cosf(a) * w, ay = sinf(a) * w; - nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, u0, 1, ax / w, t - dir * ay / w); dst++; - nvg__vset(dst, px, py, 0.5f, 1 , 0, t); dst++; - } - nvg__vset(dst, px + dlx*w, py + dly*w, u0, 1, 1, t); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1, 1, -1, t); dst++; - return dst; -} - -static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, - float dx, float dy, float w, int ncap, - float aa, float u0, float u1, float t, int dir) -{ - int i; - float px = p->x; - float py = p->y; - float dlx = dy; - float dly = -dx; - NVG_NOTUSED(aa); - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1, w, t); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1, -w, t); dst++; - for (i = 0; i < ncap; i++) { - float a = i/(float)(ncap-1)*NVG_PI; - float ax = cosf(a) * w, ay = sinf(a) * w; - nvg__vset(dst, px, py, 0.5f, 1, 0, t); dst++; - nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, u0, 1, ax / w, t + dir * ay / w); dst++; - } - return dst; -} - - -static void nvg__calculateJoins(NVGcontext* ctx, float w, int lineJoin, float miterLimit) -{ - NVGpathCache* cache = ctx->cache; - int i, j; - float iw = 0.0f; - - if (w > 0.0f) iw = 1.0f / w; - - // Calculate which joins needs extra vertices to append, and gather vertex count. - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - NVGpoint* pts = &cache->points[path->first]; - NVGpoint* p0 = &pts[path->count-1]; - NVGpoint* p1 = &pts[0]; - int nleft = 0; - - path->nbevel = 0; - - for (j = 0; j < path->count; j++) { - float dlx0, dly0, dlx1, dly1, dmr2, cross, limit; - dlx0 = p0->dy; - dly0 = -p0->dx; - dlx1 = p1->dy; - dly1 = -p1->dx; - // Calculate extrusions - p1->dmx = (dlx0 + dlx1) * 0.5f; - p1->dmy = (dly0 + dly1) * 0.5f; - dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; - if (dmr2 > 0.000001f) { - float scale = 1.0f / dmr2; - if (scale > 600.0f) { - scale = 600.0f; - } - p1->dmx *= scale; - p1->dmy *= scale; - } - - // Clear flags, but keep the corner. - p1->flags = (p1->flags & NVG_PT_CORNER) ? NVG_PT_CORNER : 0; - - // Keep track of left turns. - cross = p1->dx * p0->dy - p0->dx * p1->dy; - if (cross > 0.0f) { - nleft++; - p1->flags |= NVG_PT_LEFT; - } - - // Calculate if we should use bevel or miter for inner join. - limit = nvg__maxf(1.01f, nvg__minf(p0->len, p1->len) * iw); - if ((dmr2 * limit*limit) < 1.0f) - p1->flags |= NVG_PR_INNERBEVEL; - - // Check to see if the corner needs to be beveled. - if (p1->flags & NVG_PT_CORNER) { - if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NVG_BEVEL || lineJoin == NVG_ROUND) { - p1->flags |= NVG_PT_BEVEL; - } - } - - if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) - path->nbevel++; - - p0 = p1++; - } - - path->convex = (nleft == path->count) ? 1 : 0; - } -} - - - -static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, int lineStyle, float miterLimit) -{ - NVGpathCache* cache = ctx->cache; - NVGvertex* verts; - NVGvertex* dst; - int cverts, i, j; - float t; - float aa = fringe;//ctx->fringeWidth; - float u0 = 0.0f, u1 = 1.0f; - int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle. - - w += aa * 0.5f; - const float invStrokeWidth = 1.0 / w; - // Disable the gradient used for antialiasing when antialiasing is not used. - if (aa == 0.0f) { - u0 = 0.5f; - u1 = 0.5f; - } - - // Force round join to minimize distortion - if(lineStyle > 1) lineJoin = NVG_ROUND; - - nvg__calculateJoins(ctx, w, lineJoin, miterLimit); - // Calculate max vertex usage. - cverts = 0; - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - int loop = (path->closed == 0) ? 0 : 1; - if (lineJoin == NVG_ROUND) { - cverts += (path->count + path->nbevel*(ncap+2) + 1) * 2; // plus one for loop - } else { - cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop - } - if(lineStyle > 1) cverts += 4 * path->count; // extra vertices for spacers - if (loop == 0) { - // space for caps - if (lineCap == NVG_ROUND) { - cverts += (ncap*2 + 2)*2; - } else { - cverts += (3+3)*2; - } - } - } - - verts = nvg__allocTempVerts(ctx, cverts); - if (verts == NULL) return 0; - - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - NVGpoint* pts = &cache->points[path->first]; - NVGpoint* p0; - NVGpoint* p1; - int s, e, loop; - float dx, dy; - - path->fill = 0; - path->nfill = 0; - - // Calculate fringe or stroke - loop = (path->closed == 0) ? 0 : 1; - dst = verts; - path->stroke = dst; - - if (loop) { - // Looping - p0 = &pts[path->count-1]; - p1 = &pts[0]; - s = 0; - e = path->count; - } else { - // Add cap - p0 = &pts[0]; - p1 = &pts[1]; - s = 1; - e = path->count-1; - } - - t = 0; - - int dir = 1; - if(lineStyle > 1 && path->reversed) { - dir = -1; - for (j = s; j < path->count; ++j) { - dx = p1->x - p0->x; - dy = p1->y - p0->y; - t+=nvg__normalize(&dx, &dy) * invStrokeWidth; - p0 = p1++; - } - if (loop) { - // Looping - p0 = &pts[path->count-1]; - p1 = &pts[0]; - } else { - // Add cap - p0 = &pts[0]; - p1 = &pts[1]; - } - } - - if (loop == 0) { - // Add cap - dx = p1->x - p0->x; - dy = p1->y - p0->y; - nvg__normalize(&dx, &dy); - if (lineCap == NVG_BUTT) - dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa, u0, u1, t, dir); - else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) - dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1, t, dir); - else if (lineCap == NVG_ROUND) - dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1, t, dir); - } - for (j = s; j < e; ++j) { - if(lineStyle > 1){ - dx = p1->x - p0->x; - dy = p1->y - p0->y; - float dt=nvg__normalize(&dx, &dy); - dst = nvg_insertSpacer(dst, p0, dx, dy, w, u0, u1, t); - t+=dir*dt*invStrokeWidth; - dst = nvg_insertSpacer(dst, p1, dx, dy, w, u0, u1, t); - } - if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { - if (lineJoin == NVG_ROUND) { - dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa, t); - } else { - dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa, t); - } - } else { - nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), u0, 1, -1, t); dst++; - nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), u1, 1, 1, t); dst++; - } - p0 = p1++; - } - - if (loop) { - // Loop it - nvg__vset(dst, verts[0].x, verts[0].y, u0, 1, -1, t); dst++; - nvg__vset(dst, verts[1].x, verts[1].y, u1, 1, 1, t); dst++; - } else { - dx = p1->x - p0->x; - dy = p1->y - p0->y; - float dt = nvg__normalize(&dx, &dy); - if(lineStyle > 1){ - dst = nvg_insertSpacer(dst, p0, dx, dy, w, u0, u1, t); - t+=dir*dt*invStrokeWidth; - dst = nvg_insertSpacer(dst, p1, dx, dy, w, u0, u1, t); - } - // Add cap - if (lineCap == NVG_BUTT) - dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1, t, dir); - else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) - dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1, t, dir); - else if (lineCap == NVG_ROUND) - dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1, t, dir); - } - path->nstroke = (int)(dst - verts); - verts = dst; - } - - return 1; -} - -static int nvg__expandFill(NVGcontext* ctx, float w, int lineJoin, float miterLimit) -{ - NVGpathCache* cache = ctx->cache; - NVGvertex* verts; - NVGvertex* dst; - int cverts, convex, i, j; - float aa = ctx->fringeWidth; - int fringe = w > 0.0f; - - nvg__calculateJoins(ctx, w, lineJoin, miterLimit); - - // Calculate max vertex usage. - cverts = 0; - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - cverts += path->count + path->nbevel + 1; - if (fringe) - cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop - } - - verts = nvg__allocTempVerts(ctx, cverts); - if (verts == NULL) return 0; - - convex = cache->npaths == 1 && cache->paths[0].convex; - - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - NVGpoint* pts = &cache->points[path->first]; - NVGpoint* p0; - NVGpoint* p1; - float rw, lw, woff; - float ru, lu; - - // Calculate shape vertices. - woff = 0.5f*aa; - dst = verts; - path->fill = dst; - - if (fringe) { - // Looping - p0 = &pts[path->count-1]; - p1 = &pts[0]; - for (j = 0; j < path->count; ++j) { - if (p1->flags & NVG_PT_BEVEL) { - float dlx0 = p0->dy; - float dly0 = -p0->dx; - float dlx1 = p1->dy; - float dly1 = -p1->dx; - if (p1->flags & NVG_PT_LEFT) { - float lx = p1->x + p1->dmx * woff; - float ly = p1->y + p1->dmy * woff; - nvg__vset(dst, lx, ly, 0.5f, 1, 0, 0); dst++; - } else { - float lx0 = p1->x + dlx0 * woff; - float ly0 = p1->y + dly0 * woff; - float lx1 = p1->x + dlx1 * woff; - float ly1 = p1->y + dly1 * woff; - nvg__vset(dst, lx0, ly0, 0.5f, 1, 0, 0); dst++; - nvg__vset(dst, lx1, ly1, 0.5f, 1, 0, 0); dst++; - } - } else { - nvg__vset(dst, p1->x + (p1->dmx * woff), p1->y + (p1->dmy * woff), 0.5f,1, 0, 0); dst++; - } - p0 = p1++; - } - } else { - for (j = 0; j < path->count; ++j) { - nvg__vset(dst, pts[j].x, pts[j].y, 0.5f, 1, 0, 0); - dst++; - } - } - - path->nfill = (int)(dst - verts); - verts = dst; - - // Calculate fringe - if (fringe) { - lw = w + woff; - rw = w - woff; - lu = 0; - ru = 1; - dst = verts; - path->stroke = dst; - - // Create only half a fringe for convex shapes so that - // the shape can be rendered without stenciling. - if (convex) { - lw = woff; // This should generate the same vertex as fill inset above. - lu = 0.5f; // Set outline fade at middle. - } - - // Looping - p0 = &pts[path->count-1]; - p1 = &pts[0]; - - for (j = 0; j < path->count; ++j) { - if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { - dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx->fringeWidth, 0); - } else { - nvg__vset(dst, p1->x + (p1->dmx * lw), p1->y + (p1->dmy * lw), lu,1,0, 0); dst++; - nvg__vset(dst, p1->x - (p1->dmx * rw), p1->y - (p1->dmy * rw), ru,1,0, 0); dst++; - } - p0 = p1++; - } - - // Loop it - nvg__vset(dst, verts[0].x, verts[0].y, lu,1,0, 0); dst++; - nvg__vset(dst, verts[1].x, verts[1].y, ru,1,0, 0); dst++; - - path->nstroke = (int)(dst - verts); - verts = dst; - } else { - path->stroke = NULL; - path->nstroke = 0; - } - } - - return 1; -} - - -// Draw -void nvgBeginPath(NVGcontext* ctx) -{ - ctx->ncommands = 0; - nvg__clearPathCache(ctx); -} - -void nvgMoveTo(NVGcontext* ctx, float x, float y) -{ - float vals[] = { NVG_MOVETO, x, y }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgLineTo(NVGcontext* ctx, float x, float y) -{ - float vals[] = { NVG_LINETO, x, y }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y) -{ - float vals[] = { NVG_BEZIERTO, c1x, c1y, c2x, c2y, x, y }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y) -{ - float x0 = ctx->commandx; - float y0 = ctx->commandy; - float vals[] = { NVG_BEZIERTO, - x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0), - x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y), - x, y }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius) -{ - float x0 = ctx->commandx; - float y0 = ctx->commandy; - float dx0,dy0, dx1,dy1, a, d, cx,cy, a0,a1; - int dir; - - if (ctx->ncommands == 0) { - return; - } - - // Handle degenerate cases. - if (nvg__ptEquals(x0,y0, x1,y1, ctx->distTol) || - nvg__ptEquals(x1,y1, x2,y2, ctx->distTol) || - nvg__distPtSeg(x1,y1, x0,y0, x2,y2) < ctx->distTol*ctx->distTol || - radius < ctx->distTol) { - nvgLineTo(ctx, x1,y1); - return; - } - - // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2). - dx0 = x0-x1; - dy0 = y0-y1; - dx1 = x2-x1; - dy1 = y2-y1; - nvg__normalize(&dx0,&dy0); - nvg__normalize(&dx1,&dy1); - a = nvg__acosf(dx0*dx1 + dy0*dy1); - d = radius / nvg__tanf(a/2.0f); - -// printf("a=%f° d=%f\n", a/NVG_PI*180.0f, d); - - if (d > 10000.0f) { - nvgLineTo(ctx, x1,y1); - return; - } - - if (nvg__cross(dx0,dy0, dx1,dy1) > 0.0f) { - cx = x1 + dx0*d + dy0*radius; - cy = y1 + dy0*d + -dx0*radius; - a0 = nvg__atan2f(dx0, -dy0); - a1 = nvg__atan2f(-dx1, dy1); - dir = NVG_CW; -// printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f); - } else { - cx = x1 + dx0*d + -dy0*radius; - cy = y1 + dy0*d + dx0*radius; - a0 = nvg__atan2f(-dx0, dy0); - a1 = nvg__atan2f(dx1, -dy1); - dir = NVG_CCW; -// printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f); - } - - nvgArc(ctx, cx, cy, radius, a0, a1, dir); -} - -void nvgClosePath(NVGcontext* ctx) -{ - float vals[] = { NVG_CLOSE }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgPathWinding(NVGcontext* ctx, int dir) -{ - float vals[] = { NVG_WINDING, (float)dir }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir) -{ - float a = 0, da = 0, hda = 0, kappa = 0; - float dx = 0, dy = 0, x = 0, y = 0, tanx = 0, tany = 0; - float px = 0, py = 0, ptanx = 0, ptany = 0; - float vals[3 + 5*7 + 100]; - int i, ndivs, nvals; - int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO; - - // Clamp angles - da = a1 - a0; - if (dir == NVG_CW) { - if (nvg__absf(da) >= NVG_PI*2) { - da = NVG_PI*2; - } else { - while (da < 0.0f) da += NVG_PI*2; - } - } else { - if (nvg__absf(da) >= NVG_PI*2) { - da = -NVG_PI*2; - } else { - while (da > 0.0f) da -= NVG_PI*2; - } - } - - // Split arc into max 90 degree segments. - ndivs = nvg__maxi(1, nvg__mini((int)(nvg__absf(da) / (NVG_PI*0.5f) + 0.5f), 5)); - hda = (da / (float)ndivs) / 2.0f; - kappa = nvg__absf(4.0f / 3.0f * (1.0f - nvg__cosf(hda)) / nvg__sinf(hda)); - - if (dir == NVG_CCW) - kappa = -kappa; - - nvals = 0; - for (i = 0; i <= ndivs; i++) { - a = a0 + da * (i/(float)ndivs); - dx = nvg__cosf(a); - dy = nvg__sinf(a); - x = cx + dx*r; - y = cy + dy*r; - tanx = -dy*r*kappa; - tany = dx*r*kappa; - - if (i == 0) { - vals[nvals++] = (float)move; - vals[nvals++] = x; - vals[nvals++] = y; - } else { - vals[nvals++] = NVG_BEZIERTO; - vals[nvals++] = px+ptanx; - vals[nvals++] = py+ptany; - vals[nvals++] = x-tanx; - vals[nvals++] = y-tany; - vals[nvals++] = x; - vals[nvals++] = y; - } - px = x; - py = y; - ptanx = tanx; - ptany = tany; - } - - nvg__appendCommands(ctx, vals, nvals); -} - -void nvgRect(NVGcontext* ctx, float x, float y, float w, float h) -{ - float vals[] = { - NVG_MOVETO, x,y, - NVG_LINETO, x,y+h, - NVG_LINETO, x+w,y+h, - NVG_LINETO, x+w,y, - NVG_CLOSE - }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r) -{ - nvgRoundedRectVarying(ctx, x, y, w, h, r, r, r, r); -} - -void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft) -{ - if(radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) { - nvgRect(ctx, x, y, w, h); - return; - } else { - float halfw = nvg__absf(w)*0.5f; - float halfh = nvg__absf(h)*0.5f; - float rxBL = nvg__minf(radBottomLeft, halfw) * nvg__signf(w), ryBL = nvg__minf(radBottomLeft, halfh) * nvg__signf(h); - float rxBR = nvg__minf(radBottomRight, halfw) * nvg__signf(w), ryBR = nvg__minf(radBottomRight, halfh) * nvg__signf(h); - float rxTR = nvg__minf(radTopRight, halfw) * nvg__signf(w), ryTR = nvg__minf(radTopRight, halfh) * nvg__signf(h); - float rxTL = nvg__minf(radTopLeft, halfw) * nvg__signf(w), ryTL = nvg__minf(radTopLeft, halfh) * nvg__signf(h); - float vals[] = { - NVG_MOVETO, x, y + ryTL, - NVG_LINETO, x, y + h - ryBL, - NVG_BEZIERTO, x, y + h - ryBL*(1 - NVG_KAPPA90), x + rxBL*(1 - NVG_KAPPA90), y + h, x + rxBL, y + h, - NVG_LINETO, x + w - rxBR, y + h, - NVG_BEZIERTO, x + w - rxBR*(1 - NVG_KAPPA90), y + h, x + w, y + h - ryBR*(1 - NVG_KAPPA90), x + w, y + h - ryBR, - NVG_LINETO, x + w, y + ryTR, - NVG_BEZIERTO, x + w, y + ryTR*(1 - NVG_KAPPA90), x + w - rxTR*(1 - NVG_KAPPA90), y, x + w - rxTR, y, - NVG_LINETO, x + rxTL, y, - NVG_BEZIERTO, x + rxTL*(1 - NVG_KAPPA90), y, x, y + ryTL*(1 - NVG_KAPPA90), x, y + ryTL, - NVG_CLOSE - }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); - } -} - -void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry) -{ - float vals[] = { - NVG_MOVETO, cx-rx, cy, - NVG_BEZIERTO, cx-rx, cy+ry*NVG_KAPPA90, cx-rx*NVG_KAPPA90, cy+ry, cx, cy+ry, - NVG_BEZIERTO, cx+rx*NVG_KAPPA90, cy+ry, cx+rx, cy+ry*NVG_KAPPA90, cx+rx, cy, - NVG_BEZIERTO, cx+rx, cy-ry*NVG_KAPPA90, cx+rx*NVG_KAPPA90, cy-ry, cx, cy-ry, - NVG_BEZIERTO, cx-rx*NVG_KAPPA90, cy-ry, cx-rx, cy-ry*NVG_KAPPA90, cx-rx, cy, - NVG_CLOSE - }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgCircle(NVGcontext* ctx, float cx, float cy, float r) -{ - nvgEllipse(ctx, cx,cy, r,r); -} - -void nvgDebugDumpPathCache(NVGcontext* ctx) -{ - const NVGpath* path; - int i, j; - - printf("Dumping %d cached paths\n", ctx->cache->npaths); - for (i = 0; i < ctx->cache->npaths; i++) { - path = &ctx->cache->paths[i]; - printf(" - Path %d\n", i); - if (path->nfill) { - printf(" - fill: %d\n", path->nfill); - for (j = 0; j < path->nfill; j++) - printf("%f\t%f\n", path->fill[j].x, path->fill[j].y); - } - if (path->nstroke) { - printf(" - stroke: %d\n", path->nstroke); - for (j = 0; j < path->nstroke; j++) - printf("%f\t%f\n", path->stroke[j].x, path->stroke[j].y); - } - } -} - -void nvgFill(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - const NVGpath* path; - NVGpaint fillPaint = state->fill; - int i; - - nvg__flattenPaths(ctx); - if (ctx->params.edgeAntiAlias && state->shapeAntiAlias) - nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f); - else - nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); - - // Apply global alpha - fillPaint.innerColor.a *= state->alpha; - fillPaint.outerColor.a *= state->alpha; - - ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, - ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); - - // Count triangles - for (i = 0; i < ctx->cache->npaths; i++) { - path = &ctx->cache->paths[i]; - ctx->fillTriCount += path->nfill-2; - ctx->fillTriCount += path->nstroke-2; - ctx->drawCallCount += 2; - } -} - -void nvgStroke(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - const float scale = nvg__getAverageScale(state->xform); - float strokeWidth = nvg__clampf(state->strokeWidth * scale, 0.0f, 1000.0f); - NVGpaint strokePaint = state->stroke; - const NVGpath* path; - int i; - - - if (strokeWidth < ctx->fringeWidth) { - // If the stroke width is less than pixel size, use alpha to emulate coverage. - // Since coverage is area, scale by alpha*alpha. - float alpha = nvg__clampf(strokeWidth / ctx->fringeWidth, 0.0f, 1.0f); - strokePaint.innerColor.a *= alpha*alpha; - strokePaint.outerColor.a *= alpha*alpha; - strokeWidth = ctx->fringeWidth; - } - - // Apply global alpha - strokePaint.innerColor.a *= state->alpha; - strokePaint.outerColor.a *= state->alpha; - - nvg__flattenPaths(ctx); - - if (ctx->params.edgeAntiAlias && state->shapeAntiAlias) - nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->lineStyle, state->miterLimit); - else - nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->lineStyle, state->miterLimit); - - ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, - strokeWidth, state->lineStyle, ctx->cache->paths, ctx->cache->npaths); - - // Count triangles - for (i = 0; i < ctx->cache->npaths; i++) { - path = &ctx->cache->paths[i]; - ctx->strokeTriCount += path->nstroke-2; - ctx->drawCallCount++; - } -} - -// Add fonts -int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename) -{ - return fonsAddFont(ctx->fs, name, filename, 0); -} - -int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex) -{ - return fonsAddFont(ctx->fs, name, filename, fontIndex); -} - -int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData) -{ - return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, 0); -} - -int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex) -{ - return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, fontIndex); -} - -int nvgFindFont(NVGcontext* ctx, const char* name) -{ - if (name == NULL) return -1; - return fonsGetFontByName(ctx->fs, name); -} - - -int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont) -{ - if(baseFont == -1 || fallbackFont == -1) return 0; - return fonsAddFallbackFont(ctx->fs, baseFont, fallbackFont); -} - -int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont) -{ - return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont)); -} - -void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont) -{ - fonsResetFallbackFont(ctx->fs, baseFont); -} - -void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont) -{ - nvgResetFallbackFontsId(ctx, nvgFindFont(ctx, baseFont)); -} - -// State setting -void nvgFontSize(NVGcontext* ctx, float size) -{ - NVGstate* state = nvg__getState(ctx); - state->fontSize = size; -} - -void nvgFontBlur(NVGcontext* ctx, float blur) -{ - NVGstate* state = nvg__getState(ctx); - state->fontBlur = blur; -} - -void nvgFontDilate(NVGcontext* ctx, float dilate) -{ - NVGstate* state = nvg__getState(ctx); - state->fontDilate = dilate; -} - -void nvgTextLetterSpacing(NVGcontext* ctx, float spacing) -{ - NVGstate* state = nvg__getState(ctx); - state->letterSpacing = spacing; -} - -void nvgTextLineHeight(NVGcontext* ctx, float lineHeight) -{ - NVGstate* state = nvg__getState(ctx); - state->lineHeight = lineHeight; -} - -void nvgTextAlign(NVGcontext* ctx, int align) -{ - NVGstate* state = nvg__getState(ctx); - state->textAlign = align; -} - -void nvgFontFaceId(NVGcontext* ctx, int font) -{ - NVGstate* state = nvg__getState(ctx); - state->fontId = font; -} - -void nvgFontFace(NVGcontext* ctx, const char* font) -{ - NVGstate* state = nvg__getState(ctx); - state->fontId = fonsGetFontByName(ctx->fs, font); -} - -static float nvg__quantize(float a, float d) -{ - return ((int)(a / d + 0.5f)) * d; -} - -static float nvg__getFontScale(NVGstate* state) -{ - return nvg__minf(nvg__quantize(nvg__getAverageScale(state->xform), 0.01f), 4.0f); -} - -static void nvg__flushTextTexture(NVGcontext* ctx) -{ - int dirty[4]; - - if (fonsValidateTexture(ctx->fs, dirty)) { - int fontImage = ctx->fontImages[ctx->fontImageIdx]; - // Update texture - if (fontImage != 0) { - int iw, ih; - const unsigned char* data = fonsGetTextureData(ctx->fs, &iw, &ih); - int x = dirty[0]; - int y = dirty[1]; - int w = dirty[2] - dirty[0]; - int h = dirty[3] - dirty[1]; - ctx->params.renderUpdateTexture(ctx->params.userPtr, fontImage, x,y, w,h, data); - } - } -} - -static int nvg__allocTextAtlas(NVGcontext* ctx) -{ - int iw, ih; - nvg__flushTextTexture(ctx); - if (ctx->fontImageIdx >= NVG_MAX_FONTIMAGES-1) - return 0; - // if next fontImage already have a texture - if (ctx->fontImages[ctx->fontImageIdx+1] != 0) - nvgImageSize(ctx, ctx->fontImages[ctx->fontImageIdx+1], &iw, &ih); - else { // calculate the new font image size and create it. - nvgImageSize(ctx, ctx->fontImages[ctx->fontImageIdx], &iw, &ih); - if (iw > ih) - ih *= 2; - else - iw *= 2; - if (iw > NVG_MAX_FONTIMAGE_SIZE || ih > NVG_MAX_FONTIMAGE_SIZE) - iw = ih = NVG_MAX_FONTIMAGE_SIZE; - ctx->fontImages[ctx->fontImageIdx+1] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, iw, ih, 0, NULL); - } - ++ctx->fontImageIdx; - fonsResetAtlas(ctx->fs, iw, ih); - return 1; -} - -static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) -{ - NVGstate* state = nvg__getState(ctx); - NVGpaint paint = state->fill; - - // Render triangles. - paint.image = ctx->fontImages[ctx->fontImageIdx]; - - // Apply global alpha - paint.innerColor.a *= state->alpha; - paint.outerColor.a *= state->alpha; - - ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth); - - ctx->drawCallCount++; - ctx->textTriCount += nverts/3; -} - -static int nvg__isTransformFlipped(const float *xform) -{ - float det = xform[0] * xform[3] - xform[2] * xform[1]; - return( det < 0); -} - -float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end) -{ - NVGstate* state = nvg__getState(ctx); - FONStextIter iter, prevIter; - FONSquad q; - NVGvertex* verts; - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - int cverts = 0; - int nverts = 0; - int isFlipped = nvg__isTransformFlipped(state->xform); - - if (end == NULL) - end = string + strlen(string); - - if (state->fontId == FONS_INVALID) return x; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetDilate(ctx->fs, state->fontDilate*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - cverts = nvg__maxi(2, (int)(end - string)) * 6; // conservative estimate. - verts = nvg__allocTempVerts(ctx, cverts); - if (verts == NULL) return x; - - fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_REQUIRED); - prevIter = iter; - while (fonsTextIterNext(ctx->fs, &iter, &q)) { - float c[4*2]; - if (iter.prevGlyphIndex == -1) { // can not retrieve glyph? - if (nverts != 0) { - nvg__renderText(ctx, verts, nverts); - nverts = 0; - } - if (!nvg__allocTextAtlas(ctx)) - break; // no memory :( - iter = prevIter; - fonsTextIterNext(ctx->fs, &iter, &q); // try again - if (iter.prevGlyphIndex == -1) // still can not find glyph? - break; - } - prevIter = iter; - if(isFlipped) { - float tmp; - - tmp = q.y0; q.y0 = q.y1; q.y1 = tmp; - tmp = q.t0; q.t0 = q.t1; q.t1 = tmp; - } - // Transform corners. - nvgTransformPoint(&c[0],&c[1], state->xform, q.x0*invscale + x, q.y0*invscale + y); - nvgTransformPoint(&c[2],&c[3], state->xform, q.x1*invscale + x, q.y0*invscale + y); - nvgTransformPoint(&c[4],&c[5], state->xform, q.x1*invscale + x, q.y1*invscale + y); - nvgTransformPoint(&c[6],&c[7], state->xform, q.x0*invscale + x, q.y1*invscale + y); - // Create triangles - if (nverts+6 <= cverts) { - nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0, 0, 0); nverts++; - nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1, 0, 0); nverts++; - nvg__vset(&verts[nverts], c[2], c[3], q.s1, q.t0, 0, 0); nverts++; - nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0, 0, 0); nverts++; - nvg__vset(&verts[nverts], c[6], c[7], q.s0, q.t1, 0, 0); nverts++; - nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1, 0, 0); nverts++; - } - } - - // TODO: add back-end bit to do this just once per frame. - nvg__flushTextTexture(ctx); - - nvg__renderText(ctx, verts, nverts); - return iter.nextx * invscale + x; -} - -void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end) -{ - NVGstate* state = nvg__getState(ctx); - NVGtextRow rows[2]; - int nrows = 0, i; - int oldAlign = state->textAlign; - int halign = state->textAlign & (NVG_ALIGN_LEFT | NVG_ALIGN_CENTER | NVG_ALIGN_RIGHT); - int valign = state->textAlign & (NVG_ALIGN_TOP | NVG_ALIGN_MIDDLE | NVG_ALIGN_MIDDLE_ASCENT | NVG_ALIGN_BOTTOM | NVG_ALIGN_BASELINE); - float lineh = 0; - - if (state->fontId == FONS_INVALID) return; - - nvgTextMetrics(ctx, NULL, NULL, &lineh); - - state->textAlign = NVG_ALIGN_LEFT | valign; - - while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2, 0))) { - for (i = 0; i < nrows; i++) { - NVGtextRow* row = &rows[i]; - if (halign & NVG_ALIGN_LEFT) - nvgText(ctx, x, y, row->start, row->end); - else if (halign & NVG_ALIGN_CENTER) - nvgText(ctx, x + breakRowWidth*0.5f - row->width*0.5f, y, row->start, row->end); - else if (halign & NVG_ALIGN_RIGHT) - nvgText(ctx, x + breakRowWidth - row->width, y, row->start, row->end); - y += lineh * state->lineHeight; - } - string = rows[nrows-1].next; - } - - state->textAlign = oldAlign; -} - -int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions) -{ - NVGstate* state = nvg__getState(ctx); - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - FONStextIter iter, prevIter; - FONSquad q; - int npos = 0; - - if (state->fontId == FONS_INVALID) return 0; - - if (end == NULL) - end = string + strlen(string); - - if (string == end) - return 0; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL); - prevIter = iter; - while (fonsTextIterNext(ctx->fs, &iter, &q)) { - if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? - iter = prevIter; - fonsTextIterNext(ctx->fs, &iter, &q); // try again - } - prevIter = iter; - positions[npos].str = iter.str; - positions[npos].x = iter.x * invscale + x; - positions[npos].minx = nvg__minf(iter.x, q.x0) * invscale + x; - positions[npos].maxx = nvg__maxf(iter.nextx, q.x1) * invscale + x; - npos++; - if (npos >= maxPositions) - break; - } - - return npos; -} - -enum NVGcodepointType { - NVG_SPACE, - NVG_NEWLINE, - NVG_CHAR, - NVG_CJK_CHAR, -}; - -int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows, int skipSpaces) -{ - NVGstate* state = nvg__getState(ctx); - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - FONStextIter iter, prevIter; - FONSquad q; - int nrows = 0; - float rowStartX = 0; - float rowWidth = 0; - float rowMinX = 0; - float rowMaxX = 0; - const char* rowStart = NULL; - const char* rowEnd = NULL; - const char* wordStart = NULL; - float wordStartX = 0; - float wordMinX = 0; - const char* breakEnd = NULL; - float breakWidth = 0; - float breakMaxX = 0; - int type = NVG_SPACE, ptype = NVG_SPACE; - unsigned int pcodepoint = 0; - - if (maxRows == 0) return 0; - if (state->fontId == FONS_INVALID) return 0; - - if (end == NULL) - end = string + strlen(string); - - if (string == end) return 0; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetDilate(ctx->fs, state->fontDilate*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - breakRowWidth *= scale; - - fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL); - prevIter = iter; - while (fonsTextIterNext(ctx->fs, &iter, &q)) { - if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? - iter = prevIter; - fonsTextIterNext(ctx->fs, &iter, &q); // try again - } - prevIter = iter; - switch (iter.codepoint) { - case 9: // \t - case 11: // \v - case 12: // \f - case 32: // space - case 0x00a0: // NBSP - type = NVG_SPACE; - break; - case 10: // \n - type = pcodepoint == 13 ? NVG_SPACE : NVG_NEWLINE; - break; - case 13: // \r - type = pcodepoint == 10 ? NVG_SPACE : NVG_NEWLINE; - break; - case 0x0085: // NEL - type = NVG_NEWLINE; - break; - default: - if ((iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) || - (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) || - (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) || - (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) || - (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) || - (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF)) - type = NVG_CJK_CHAR; - else - type = NVG_CHAR; - break; - } - - if (type == NVG_NEWLINE) { - // Always handle new lines. - rows[nrows].start = rowStart != NULL ? rowStart : iter.str; - rows[nrows].end = rowEnd != NULL ? rowEnd : iter.str; - rows[nrows].width = rowWidth * invscale; - rows[nrows].minx = rowMinX * invscale; - rows[nrows].maxx = rowMaxX * invscale; - rows[nrows].next = iter.next; - nrows++; - if (nrows >= maxRows) - return nrows; - // Set null break point - breakEnd = rowStart; - breakWidth = 0.0; - breakMaxX = 0.0; - // Indicate to skip the white space at the beginning of the row. - rowStart = NULL; - rowEnd = NULL; - rowWidth = 0; - rowMinX = rowMaxX = 0; - } else { - if (rowStart == NULL) { - // Skip white space until the beginning of the line - if (type == NVG_CHAR || type == NVG_CJK_CHAR) { - // The current char is the row so far - rowStartX = iter.x; - rowStart = iter.str; - rowEnd = iter.next; - rowWidth = iter.nextx - rowStartX; - rowMinX = q.x0 - rowStartX; - rowMaxX = q.x1 - rowStartX; - wordStart = iter.str; - wordStartX = iter.x; - wordMinX = q.x0 - rowStartX; - // Set null break point - breakEnd = rowStart; - breakWidth = 0.0; - breakMaxX = 0.0; - } - } else { - float nextWidth = iter.nextx - rowStartX; - - // track last non-white space character - if (type == NVG_CHAR || type == NVG_CJK_CHAR) { - rowEnd = iter.next; - rowWidth = iter.nextx - rowStartX; - rowMaxX = q.x1 - rowStartX; - } - // track last end of a word - if (((ptype == NVG_CHAR || ptype == NVG_CJK_CHAR) && type == NVG_SPACE) || type == NVG_CJK_CHAR) { - breakEnd = iter.str; - breakWidth = rowWidth; - breakMaxX = rowMaxX; - } - // track last beginning of a word - if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) { - wordStart = iter.str; - wordStartX = iter.x; - wordMinX = q.x0; - } - - // Break to new line when a character is beyond break width. - if ((type == NVG_CHAR || type == NVG_CJK_CHAR) && nextWidth > breakRowWidth) { - // The run length is too long, need to break to new line. - if (breakEnd == rowStart) { - // The current word is longer than the row length, just break it from here. - rows[nrows].start = rowStart; - rows[nrows].end = iter.str; - rows[nrows].width = rowWidth * invscale; - rows[nrows].minx = rowMinX * invscale; - rows[nrows].maxx = rowMaxX * invscale; - rows[nrows].next = iter.str; - nrows++; - if (nrows >= maxRows) - return nrows; - rowStartX = iter.x; - rowStart = iter.str; - rowEnd = iter.next; - rowWidth = iter.nextx - rowStartX; - rowMinX = q.x0 - rowStartX; - rowMaxX = q.x1 - rowStartX; - wordStart = iter.str; - wordStartX = iter.x; - wordMinX = q.x0 - rowStartX; - } else { - // Break the line from the end of the last word, and start new line from the beginning of the new. - rows[nrows].start = rowStart; - rows[nrows].end = breakEnd; - rows[nrows].width = breakWidth * invscale; - rows[nrows].minx = rowMinX * invscale; - rows[nrows].maxx = breakMaxX * invscale; - rows[nrows].next = wordStart; - nrows++; - if (nrows >= maxRows) - return nrows; - // Update row - rowStartX = wordStartX; - rowStart = wordStart; - rowEnd = iter.next; - rowWidth = iter.nextx - rowStartX; - rowMinX = wordMinX - rowStartX; - rowMaxX = q.x1 - rowStartX; - } - // Set null break point - breakEnd = rowStart; - breakWidth = 0.0; - breakMaxX = 0.0; - } - } - } - - pcodepoint = iter.codepoint; - ptype = type; - } - - // Break the line from the end of the last word, and start new line from the beginning of the new. - if (rowStart != NULL) { - rows[nrows].start = rowStart; - rows[nrows].end = rowEnd; - rows[nrows].width = rowWidth * invscale; - rows[nrows].minx = rowMinX * invscale; - rows[nrows].maxx = rowMaxX * invscale; - rows[nrows].next = end; - nrows++; - } else if (!skipSpaces) { - rows[nrows].start = end; - rows[nrows].end = end; - rows[nrows].width = rowWidth * invscale; - rows[nrows].minx = rowMinX * invscale; - rows[nrows].maxx = rowMaxX * invscale; - rows[nrows].next = end; - nrows++; - } - - return nrows; -} - -float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds) -{ - NVGstate* state = nvg__getState(ctx); - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - float width; - - if (state->fontId == FONS_INVALID) return 0; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetDilate(ctx->fs, state->fontDilate*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - width = fonsTextBounds(ctx->fs, 0, 0, string, end, bounds); - if (bounds != NULL) { - // Use line bounds for height. - fonsLineBounds(ctx->fs, 0, &bounds[1], &bounds[3]); - bounds[0] = bounds[0] * invscale + x; - bounds[1] = bounds[1] * invscale + y; - bounds[2] = bounds[2] * invscale + x; - bounds[3] = bounds[3] * invscale + y; - } - return width * invscale; -} - -void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds) -{ - NVGstate* state = nvg__getState(ctx); - NVGtextRow rows[2]; - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - float yoff = 0; - int nrows = 0, i; - int oldAlign = state->textAlign; - int halign = state->textAlign & (NVG_ALIGN_LEFT | NVG_ALIGN_CENTER | NVG_ALIGN_RIGHT); - int valign = state->textAlign & (NVG_ALIGN_TOP | NVG_ALIGN_MIDDLE | NVG_ALIGN_MIDDLE_ASCENT | NVG_ALIGN_BOTTOM | NVG_ALIGN_BASELINE); - float lineh = 0, rminy = 0, rmaxy = 0; - float minx, miny, maxx, maxy; - - if (state->fontId == FONS_INVALID) { - if (bounds != NULL) - bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0f; - return; - } - - nvgTextMetrics(ctx, NULL, NULL, &lineh); - - state->textAlign = NVG_ALIGN_LEFT | valign; - - minx = maxx = 0; - miny = maxy = 0; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetDilate(ctx->fs, state->fontDilate*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - fonsLineBounds(ctx->fs, 0, &rminy, &rmaxy); - rminy *= invscale; - rmaxy *= invscale; - - while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2, 0))) { - for (i = 0; i < nrows; i++) { - NVGtextRow* row = &rows[i]; - float rminx, rmaxx, dx = 0; - // Horizontal bounds - if (halign & NVG_ALIGN_LEFT) - dx = 0; - else if (halign & NVG_ALIGN_CENTER) - dx = breakRowWidth*0.5f - row->width*0.5f; - else if (halign & NVG_ALIGN_RIGHT) - dx = breakRowWidth - row->width; - rminx = row->minx + dx; - rmaxx = row->maxx + dx; - minx = nvg__minf(minx, rminx); - maxx = nvg__maxf(maxx, rmaxx); - // Vertical bounds. - miny = nvg__minf(miny, yoff + rminy); - maxy = nvg__maxf(maxy, yoff + rmaxy); - - yoff += lineh * state->lineHeight; - } - string = rows[nrows-1].next; - } - - state->textAlign = oldAlign; - - if (bounds != NULL) { - bounds[0] = minx + x; - bounds[1] = miny + y; - bounds[2] = maxx + x; - bounds[3] = maxy + y; - } -} - -void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh) -{ - NVGstate* state = nvg__getState(ctx); - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - - if (state->fontId == FONS_INVALID) return; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetDilate(ctx->fs, state->fontDilate*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - fonsVertMetrics(ctx->fs, ascender, descender, lineh); - if (ascender != NULL) - *ascender *= invscale; - if (descender != NULL) - *descender *= invscale; - if (lineh != NULL) - *lineh *= invscale; -} -// vim: ft=c nu noet ts=4 diff --git a/src/nanovg.h b/src/nanovg.h deleted file mode 100644 index b88a65b..0000000 --- a/src/nanovg.h +++ /dev/null @@ -1,741 +0,0 @@ -// -// 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. -// - -#ifndef NANOVG_H -#define NANOVG_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define NVG_PI 3.14159265358979323846264338327f - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union -#endif - -typedef struct NVGcontext NVGcontext; - -struct NVGcolor { - union { - float rgba[4]; - struct { - float r,g,b,a; - }; - }; -}; -typedef struct NVGcolor NVGcolor; -typedef struct NVGcolor NVGcolor; - -struct NVGpaint { - float xform[6]; - float extent[2]; - float radius; - float feather; - NVGcolor innerColor; - NVGcolor outerColor; - int image; -}; -typedef struct NVGpaint NVGpaint; - -enum NVGwinding { - NVG_CCW = 1, // Winding for solid shapes - NVG_CW = 2, // Winding for holes -}; - -enum NVGsolidity { - NVG_SOLID = 1, // CCW - NVG_HOLE = 2, // CW -}; - -enum NVGlineStyle { - NVG_LINE_SOLID = 1, - NVG_LINE_DASHED = 2, - NVG_LINE_DOTTED = 3, - NVG_LINE_GLOW = 4 -}; - -enum NVGlineCap { - NVG_BUTT, - NVG_ROUND, - NVG_SQUARE, - NVG_BEVEL, - NVG_MITER, -}; - -enum NVGalign { - // Horizontal align - NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left. - NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center. - NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right. - // Vertical align - NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. - NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. - NVG_ALIGN_MIDDLE_ASCENT = 1<<5, // Align text vertically to middle of ascent. - NVG_ALIGN_BOTTOM = 1<<6, // Align text vertically to bottom. - NVG_ALIGN_BASELINE = 1<<7 // Default, align text vertically to baseline. -}; - -enum NVGblendFactor { - NVG_ZERO = 1<<0, - NVG_ONE = 1<<1, - NVG_SRC_COLOR = 1<<2, - NVG_ONE_MINUS_SRC_COLOR = 1<<3, - NVG_DST_COLOR = 1<<4, - NVG_ONE_MINUS_DST_COLOR = 1<<5, - NVG_SRC_ALPHA = 1<<6, - NVG_ONE_MINUS_SRC_ALPHA = 1<<7, - NVG_DST_ALPHA = 1<<8, - NVG_ONE_MINUS_DST_ALPHA = 1<<9, - NVG_SRC_ALPHA_SATURATE = 1<<10, -}; - -enum NVGcompositeOperation { - NVG_SOURCE_OVER, - NVG_SOURCE_IN, - NVG_SOURCE_OUT, - NVG_ATOP, - NVG_DESTINATION_OVER, - NVG_DESTINATION_IN, - NVG_DESTINATION_OUT, - NVG_DESTINATION_ATOP, - NVG_LIGHTER, - NVG_COPY, - NVG_XOR, -}; - -struct NVGcompositeOperationState { - int srcRGB; - int dstRGB; - int srcAlpha; - int dstAlpha; -}; -typedef struct NVGcompositeOperationState NVGcompositeOperationState; - -struct NVGglyphPosition { - const char* str; // Position of the glyph in the input string. - float x; // The x-coordinate of the logical glyph position. - float minx, maxx; // The bounds of the glyph shape. -}; -typedef struct NVGglyphPosition NVGglyphPosition; - -struct NVGtextRow { - const char* start; // Pointer to the input text where the row starts. - const char* end; // Pointer to the input text where the row ends (one past the last character). - const char* next; // Pointer to the beginning of the next row. - float width; // Logical width of the row. - float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. -}; -typedef struct NVGtextRow NVGtextRow; - -enum NVGimageFlags { - NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. - NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. - NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. - NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. - NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. - NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear -}; - -// Begin drawing a new frame -// Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() -// nvgBeginFrame() defines the size of the window to render to in relation currently -// set viewport (i.e. glViewport on GL backends). Device pixel ration allows to -// control the rendering on Hi-DPI devices. -// For example, GLFW returns two dimension for an opened window: window size and -// frame buffer size. In that case you would set windowWidth/Height to the window size -// devicePixelRatio to: frameBufferWidth / windowWidth. -void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio); - -// Cancels drawing the current frame. -void nvgCancelFrame(NVGcontext* ctx); - -// Ends drawing flushing remaining render state. -void nvgEndFrame(NVGcontext* ctx); - -// -// Composite operation -// -// The composite operations in NanoVG are modeled after HTML Canvas API, and -// the blend func is based on OpenGL (see corresponding manuals for more info). -// The colors in the blending state have premultiplied alpha. - -// Sets the composite operation. The op parameter should be one of NVGcompositeOperation. -void nvgGlobalCompositeOperation(NVGcontext* ctx, int op); - -// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor. -void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor); - -// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor. -void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha); - -// -// Color utils -// -// Colors in NanoVG are stored as unsigned ints in ABGR format. - -// Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f). -NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b); - -// Returns a color value from red, green, blue values. Alpha will be set to 1.0f. -NVGcolor nvgRGBf(float r, float g, float b); - - -// Returns a color value from red, green, blue and alpha values. -NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); - -// Returns a color value from red, green, blue and alpha values. -NVGcolor nvgRGBAf(float r, float g, float b, float a); - - -// Linearly interpolates from color c0 to c1, and returns resulting color value. -NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u); - -// Sets transparency of a color value. -NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a); - -// Sets transparency of a color value. -NVGcolor nvgTransRGBAf(NVGcolor c0, float a); - -// Returns color value specified by hue, saturation and lightness. -// HSL values are all in range [0..1], alpha will be set to 255. -NVGcolor nvgHSL(float h, float s, float l); - -// Returns color value specified by hue, saturation and lightness and alpha. -// HSL values are all in range [0..1], alpha in range [0..255] -NVGcolor nvgHSLA(float h, float s, float l, unsigned char a); - -// -// State Handling -// -// NanoVG contains state which represents how paths will be rendered. -// The state contains transform, fill and stroke styles, text and font styles, -// and scissor clipping. - -// Pushes and saves the current render state into a state stack. -// A matching nvgRestore() must be used to restore the state. -void nvgSave(NVGcontext* ctx); - -// Pops and restores current render state. -void nvgRestore(NVGcontext* ctx); - -// Resets current render state to default values. Does not affect the render state stack. -void nvgReset(NVGcontext* ctx); - -// Gets the current scissor bounds -struct NVGscissorBounds nvgCurrentScissor(NVGcontext* ctx); - -// -// Render styles -// -// Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern. -// Solid color is simply defined as a color value, different kinds of paints can be created -// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). -// -// Current render style can be saved and restored using nvgSave() and nvgRestore(). - -// Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default. -void nvgShapeAntiAlias(NVGcontext* ctx, int enabled); - -// Sets current stroke style to a solid color. -void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); - -// Sets current stroke style to a paint, which can be a one of the gradients or a pattern. -void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint); - -// Sets current fill style to a solid color. -void nvgFillColor(NVGcontext* ctx, NVGcolor color); - -// Sets current fill style to a paint, which can be a one of the gradients or a pattern. -void nvgFillPaint(NVGcontext* ctx, NVGpaint paint); - -// Sets the miter limit of the stroke style. -// Miter limit controls when a sharp corner is beveled. -void nvgMiterLimit(NVGcontext* ctx, float limit); - -// Sets the stroke width of the stroke style. -void nvgStrokeWidth(NVGcontext* ctx, float size); - -// Sets how line is drawn. -// Can be one of NVG_LINE_SOLID (default), NVG_LINE_GLOW, NVG_LINE_DASHED, NVG LINE_DOTTED -void nvgLineStyle(NVGcontext* ctx, int lineStyle); - -// Sets how the end of the line (cap) is drawn, -// Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. -void nvgLineCap(NVGcontext* ctx, int cap); - -// Sets how sharp path corners are drawn. -// Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL. -void nvgLineJoin(NVGcontext* ctx, int join); - -// Sets the transparency applied to all rendered shapes. -// Already transparent paths will get proportionally more transparent as well. -void nvgGlobalAlpha(NVGcontext* ctx, float alpha); - -// -// Transforms -// -// The paths, gradients, patterns and scissor region are transformed by an transformation -// matrix at the time when they are passed to the API. -// The current transformation matrix is a affine matrix: -// [sx kx tx] -// [ky sy ty] -// [ 0 0 1] -// Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. -// The last row is assumed to be 0,0,1 and is not stored. -// -// Apart from nvgResetTransform(), each transformation function first creates -// specific transformation matrix and pre-multiplies the current transformation by it. -// -// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). - -// Resets current transform to a identity matrix. -void nvgResetTransform(NVGcontext* ctx); - -// Premultiplies current coordinate system by specified matrix. -// The parameters are interpreted as matrix as follows: -// [a c e] -// [b d f] -// [0 0 1] -void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f); - -// Translates current coordinate system. -void nvgTranslate(NVGcontext* ctx, float x, float y); - -// Rotates current coordinate system. Angle is specified in radians. -void nvgRotate(NVGcontext* ctx, float angle); - -// Skews the current coordinate system along X axis. Angle is specified in radians. -void nvgSkewX(NVGcontext* ctx, float angle); - -// Skews the current coordinate system along Y axis. Angle is specified in radians. -void nvgSkewY(NVGcontext* ctx, float angle); - -// Scales the current coordinate system. -void nvgScale(NVGcontext* ctx, float x, float y); - -// Stores the top part (a-f) of the current transformation matrix in to the specified buffer. -// [a c e] -// [b d f] -// [0 0 1] -// There should be space for 6 floats in the return buffer for the values a-f. -void nvgCurrentTransform(NVGcontext* ctx, float* xform); - - -// The following functions can be used to make calculations on 2x3 transformation matrices. -// A 2x3 matrix is represented as float[6]. - -// Sets the transform to identity matrix. -void nvgTransformIdentity(float* dst); - -// Sets the transform to translation matrix matrix. -void nvgTransformTranslate(float* dst, float tx, float ty); - -// Sets the transform to scale matrix. -void nvgTransformScale(float* dst, float sx, float sy); - -// Sets the transform to rotate matrix. Angle is specified in radians. -void nvgTransformRotate(float* dst, float a); - -// Sets the transform to skew-x matrix. Angle is specified in radians. -void nvgTransformSkewX(float* dst, float a); - -// Sets the transform to skew-y matrix. Angle is specified in radians. -void nvgTransformSkewY(float* dst, float a); - -// Sets the transform to the result of multiplication of two transforms, of A = A*B. -void nvgTransformMultiply(float* dst, const float* src); - -// Sets the transform to the result of multiplication of two transforms, of A = B*A. -void nvgTransformPremultiply(float* dst, const float* src); - -// Sets the destination to inverse of specified transform. -// Returns 1 if the inverse could be calculated, else 0. -int nvgTransformInverse(float* dst, const float* src); - -// Transform a point by given transform. -void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy); - -// Converts degrees to radians and vice versa. -float nvgDegToRad(float deg); -float nvgRadToDeg(float rad); - -// -// Images -// -// NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. -// In addition you can upload your own image. The image loading is provided by stb_image. -// The parameter imageFlags is combination of flags defined in NVGimageFlags. - -// Creates image by loading it from the disk from specified file name. -// Returns handle to the image. -int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); - -// Creates image by loading it from the specified chunk of memory. -// Returns handle to the image. -int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); - -// Creates image from specified image data. -// Returns handle to the image. -int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data); - -// Updates image data specified by image handle. -void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data); - -// Returns the dimensions of a created image. -void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h); - -// Deletes created image. -void nvgDeleteImage(NVGcontext* ctx, int image); - -// -// Paints -// -// NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. -// These can be used as paints for strokes and fills. - -// Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates -// of the linear gradient, icol specifies the start color and ocol the end color. -// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). -NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey, - NVGcolor icol, NVGcolor ocol); - -// Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering -// drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, -// (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry -// the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. -// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). -NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, - float r, float f, NVGcolor icol, NVGcolor ocol); - -// Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify -// the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. -// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). -NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, - NVGcolor icol, NVGcolor ocol); - -// Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern, -// (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. -// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). -NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, - float angle, int image, float alpha); - -// -// Scissoring -// -// Scissoring allows you to clip the rendering into a rectangle. This is useful for various -// user interface cases like rendering a text edit or a timeline. - -// Sets the current scissor rectangle. -// The scissor rectangle is transformed by the current transform. -void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h); - -// Intersects current scissor rectangle with the specified rectangle. -// The scissor rectangle is transformed by the current transform. -// Note: in case the rotation of previous scissor rect differs from -// the current one, the intersection will be done between the specified -// rectangle and the previous scissor rectangle transformed in the current -// transform space. The resulting shape is always rectangle. -void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h); - -// Reset and disables scissoring. -void nvgResetScissor(NVGcontext* ctx); - -// -// Paths -// -// Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths. -// Then you define one or more paths and sub-paths which describe the shape. The are functions -// to draw common shapes like rectangles and circles, and lower level step-by-step functions, -// which allow to define a path curve by curve. -// -// NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise -// winding and holes should have counter clockwise order. To specify winding of a path you can -// call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW. -// -// Finally you can fill the path using current fill style by calling nvgFill(), and stroke it -// with current stroke style by calling nvgStroke(). -// -// The curve segments and sub-paths are transformed by the current transform. - -// Clears the current path and sub-paths. -void nvgBeginPath(NVGcontext* ctx); - -// Starts new sub-path with specified point as first point. -void nvgMoveTo(NVGcontext* ctx, float x, float y); - -// Adds line segment from the last point in the path to the specified point. -void nvgLineTo(NVGcontext* ctx, float x, float y); - -// Adds cubic bezier segment from last point in the path via two control points to the specified point. -void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y); - -// Adds quadratic bezier segment from last point in the path via a control point to the specified point. -void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y); - -// Adds an arc segment at the corner defined by the last path point, and two specified points. -void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius); - -// Closes current sub-path with a line segment. -void nvgClosePath(NVGcontext* ctx); - -// Sets the current sub-path winding, see NVGwinding and NVGsolidity. -void nvgPathWinding(NVGcontext* ctx, int dir); - -// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, -// and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW). -// Angles are specified in radians. -void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir); - -// Creates new rectangle shaped sub-path. -void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); - -// Creates new rounded rectangle shaped sub-path. -void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); - -// Creates new rounded rectangle shaped sub-path with varying radii for each corner. -void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft); - -// Creates new ellipse shaped sub-path. -void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); - -// Creates new circle shaped sub-path. -void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); - -// Fills the current path with current fill style. -void nvgFill(NVGcontext* ctx); - -// Fills the current path with current stroke style. -void nvgStroke(NVGcontext* ctx); - - -// -// Text -// -// NanoVG allows you to load .ttf files and use the font to render text. -// -// The appearance of the text can be defined by setting the current text style -// and by specifying the fill color. Common text and font settings such as -// font size, letter spacing and text align are supported. Font blur allows you -// to create simple text effects such as drop shadows. -// -// At render time the font face can be set based on the font handles or name. -// -// Font measure functions return values in local space, the calculations are -// carried in the same resolution as the final rendering. This is done because -// the text glyph positions are snapped to the nearest pixels sharp rendering. -// -// The local space means that values are not rotated or scale as per the current -// transformation. For example if you set font size to 12, which would mean that -// line height is 16, then regardless of the current scaling and rotation, the -// returned line height is always 16. Some measures may vary because of the scaling -// since aforementioned pixel snapping. -// -// While this may sound a little odd, the setup allows you to always render the -// same way regardless of scaling. I.e. following works regardless of scaling: -// -// const char* txt = "Text me up."; -// nvgTextBounds(vg, x,y, txt, NULL, bounds); -// nvgBeginPath(vg); -// nvgRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); -// nvgFill(vg); -// -// Note: currently only solid color fill is supported for text. - -// Creates font by loading it from the disk from specified file name. -// Returns handle to the font. -int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); - -// fontIndex specifies which font face to load from a .ttf/.ttc file. -int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex); - -// Creates font by loading it from the specified memory chunk. -// Returns handle to the font. -int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); - -// fontIndex specifies which font face to load from a .ttf/.ttc file. -int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex); - -// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. -int nvgFindFont(NVGcontext* ctx, const char* name); - -// Adds a fallback font by handle. -int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont); - -// Adds a fallback font by name. -int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont); - -// Resets fallback fonts by handle. -void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont); - -// Resets fallback fonts by name. -void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont); - -// Sets the font size of current text style. -void nvgFontSize(NVGcontext* ctx, float size); - -// Sets the blur of current text style. -void nvgFontBlur(NVGcontext* ctx, float blur); - -// Sets the dilation of current text style. -void nvgFontDilate(NVGcontext* ctx, float dilate); - -// Sets the letter spacing of current text style. -void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); - -// Sets the proportional line height of current text style. The line height is specified as multiple of font size. -void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); - -// Sets the text align of current text style, see NVGalign for options. -void nvgTextAlign(NVGcontext* ctx, int align); - -// Sets the font face based on specified id of current text style. -void nvgFontFaceId(NVGcontext* ctx, int font); - -// Sets the font face based on specified name of current text style. -void nvgFontFace(NVGcontext* ctx, const char* font); - -// Gets the font size of current text style. -int nvgGetFontFaceId(NVGcontext* ctx); - -// Get the font size -float nvgGetFontSize(NVGcontext* ctx); - -//Get Stroke width -float nvgGetStrokeWidth(NVGcontext* ctx); - -// Get text alignment -int nvgGetTextAlign(NVGcontext* ctx); - -// Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. -float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end); - -// Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. -// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. -// Words longer than the max width are slit at nearest character (i.e. no hyphenation). -void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end); - -// Measures the specified text string. Parameter bounds should be a pointer to float[4], -// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] -// Returns the horizontal advance of the measured text (i.e. where the next character should drawn). -// Measured values are returned in local coordinate space. -float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds); - -// Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], -// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] -// Measured values are returned in local coordinate space. -void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds); - -// Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. -// Measured values are returned in local coordinate space. -int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions); - -// Returns the vertical metrics based on the current text style. -// Measured values are returned in local coordinate space. -void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh); - -// Breaks the specified text into lines. If end is specified only the sub-string will be used. -// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. -// Words longer than the max width are slit at nearest character (i.e. no hyphenation). -int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows, int skipSpaces); - -// Get image texture Id -int nvgGetImageTextureId(NVGcontext* ctx, int handle); - -// -// Internal Render API -// -enum NVGtexture { - NVG_TEXTURE_ALPHA = 0x01, - NVG_TEXTURE_RGBA = 0x02, -}; - -struct NVGscissor { - float xform[6]; - float extent[2]; -}; -typedef struct NVGscissor NVGscissor; - -struct NVGscissorBounds { - float x; - float y; - float w; - float h; -}; -typedef struct NVGscissorBounds NVGscissorBounds; - -struct NVGvertex { - float x,y,u,v,s,t; -}; -typedef struct NVGvertex NVGvertex; - -struct NVGpath { - int first; - int count; - int reversed; - unsigned char closed; - int nbevel; - NVGvertex* fill; - int nfill; - NVGvertex* stroke; - int nstroke; - int winding; - int convex; -}; -typedef struct NVGpath NVGpath; - -struct NVGparams { - void* userPtr; - int edgeAntiAlias; - int (*renderCreate)(void* uptr); - int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); - int (*renderDeleteTexture)(void* uptr, int image); - int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); - int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); - int (*renderGetImageTextureId)(void* uptr, int handle); - void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio); - void (*renderCancel)(void* uptr); - void (*renderFlush)(void* uptr); - void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); - void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, int lineStyle, const NVGpath* paths, int npaths); - void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe); - void (*renderDelete)(void* uptr); -}; -typedef struct NVGparams NVGparams; - -// Constructor and destructor, called by the render back-end. -NVGcontext* nvgCreateInternal(NVGparams* params); -void nvgDeleteInternal(NVGcontext* ctx); - -NVGparams* nvgInternalParams(NVGcontext* ctx); - -// Debug function to dump cached path data. -void nvgDebugDumpPathCache(NVGcontext* ctx); - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; } - -#ifdef __cplusplus -} -#endif - -#endif // NANOVG_H diff --git a/src/nanovg_gl.h b/src/nanovg_gl.h deleted file mode 100644 index 31c436b..0000000 --- a/src/nanovg_gl.h +++ /dev/null @@ -1,1712 +0,0 @@ -// -// Copyright (c) 2009-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. -// -#ifndef NANOVG_GL_H -#define NANOVG_GL_H - -#ifdef __cplusplus -extern "C" { -#endif - -// Create flags - -enum NVGcreateFlags { - // Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA). - NVG_ANTIALIAS = 1<<0, - // Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little - // slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. - NVG_STENCIL_STROKES = 1<<1, - // Flag indicating that additional debug checks are done. - NVG_DEBUG = 1<<2, -}; - -#if defined NANOVG_GL2_IMPLEMENTATION -# define NANOVG_GL2 1 -# define NANOVG_GL_IMPLEMENTATION 1 -#elif defined NANOVG_GL3_IMPLEMENTATION -# define NANOVG_GL3 1 -# define NANOVG_GL_IMPLEMENTATION 1 -# define NANOVG_GL_USE_UNIFORMBUFFER 1 -#elif defined NANOVG_GLES2_IMPLEMENTATION -# define NANOVG_GLES2 1 -# define NANOVG_GL_IMPLEMENTATION 1 -#elif defined NANOVG_GLES3_IMPLEMENTATION -# define NANOVG_GLES3 1 -# define NANOVG_GL_IMPLEMENTATION 1 -#endif - -#define NANOVG_GL_USE_STATE_FILTER (1) - -// Creates NanoVG contexts for different OpenGL (ES) versions. -// Flags should be combination of the create flags above. - -#if defined NANOVG_GL2 - -NVGcontext* nvgCreateGL2(int flags); -void nvgDeleteGL2(NVGcontext* ctx); - -int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); -GLuint nvglImageHandleGL2(NVGcontext* ctx, int image); - -#endif - -#if defined NANOVG_GL3 - -NVGcontext* nvgCreateGL3(int flags); -void nvgDeleteGL3(NVGcontext* ctx); - -int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); -GLuint nvglImageHandleGL3(NVGcontext* ctx, int image); - -#endif - -#if defined NANOVG_GLES2 - -NVGcontext* nvgCreateGLES2(int flags); -void nvgDeleteGLES2(NVGcontext* ctx); - -int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); -GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image); - -#endif - -#if defined NANOVG_GLES3 - -NVGcontext* nvgCreateGLES3(int flags); -void nvgDeleteGLES3(NVGcontext* ctx); - -int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); -GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image); - -#endif - -// These are additional flags on top of NVGimageFlags. -enum NVGimageFlagsGL { - NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. -}; - -#ifdef __cplusplus -} -#endif - -#endif /* NANOVG_GL_H */ - -#ifdef NANOVG_GL_IMPLEMENTATION - -#include -#include -#include -#include -#include "nanovg.h" - -enum GLNVGuniformLoc { - GLNVG_LOC_VIEWSIZE, - GLNVG_LOC_TEX, - GLNVG_LOC_FRAG, - GLNVG_MAX_LOCS -}; - -enum GLNVGshaderType { - NSVG_SHADER_FILLGRAD, - NSVG_SHADER_FILLIMG, - NSVG_SHADER_SIMPLE, - NSVG_SHADER_IMG -}; - -#if NANOVG_GL_USE_UNIFORMBUFFER -enum GLNVGuniformBindings { - GLNVG_FRAG_BINDING = 0, -}; -#endif - -struct GLNVGshader { - GLuint prog; - GLuint frag; - GLuint vert; - GLint loc[GLNVG_MAX_LOCS]; -}; -typedef struct GLNVGshader GLNVGshader; - -struct GLNVGtexture { - int id; - GLuint tex; - int width, height; - int type; - int flags; -}; -typedef struct GLNVGtexture GLNVGtexture; - -struct GLNVGblend -{ - GLenum srcRGB; - GLenum dstRGB; - GLenum srcAlpha; - GLenum dstAlpha; -}; -typedef struct GLNVGblend GLNVGblend; - -enum GLNVGcallType { - GLNVG_NONE = 0, - GLNVG_FILL, - GLNVG_CONVEXFILL, - GLNVG_STROKE, - GLNVG_TRIANGLES, -}; - -struct GLNVGcall { - int type; - int image; - int pathOffset; - int pathCount; - int triangleOffset; - int triangleCount; - int uniformOffset; - GLNVGblend blendFunc; -}; -typedef struct GLNVGcall GLNVGcall; - -struct GLNVGpath { - int fillOffset; - int fillCount; - int strokeOffset; - int strokeCount; -}; -typedef struct GLNVGpath GLNVGpath; - -struct GLNVGfragUniforms { - #if NANOVG_GL_USE_UNIFORMBUFFER - float scissorMat[12]; // matrices are actually 3 vec4s - float paintMat[12]; - struct NVGcolor innerCol; - struct NVGcolor outerCol; - float scissorExt[2]; - float scissorScale[2]; - float extent[2]; - float radius; - float feather; - float strokeMult; - float strokeThr; - int lineStyle; - int texType; - int type; - #else - // note: after modifying layout or size of uniform array, - // don't forget to also update the fragment shader source! - #define NANOVG_GL_UNIFORMARRAY_SIZE 12 - union { - struct { - float scissorMat[12]; // matrices are actually 3 vec4s - float paintMat[12]; - struct NVGcolor innerCol; - struct NVGcolor outerCol; - float scissorExt[2]; - float scissorScale[2]; - float extent[2]; - float radius; - float feather; - float strokeMult; - float strokeThr; - float lineStyle; - float texType; - float type; - float unused1; - float unused2; - float unused3; - }; - float uniformArray[NANOVG_GL_UNIFORMARRAY_SIZE][4]; - }; - #endif -}; -typedef struct GLNVGfragUniforms GLNVGfragUniforms; - -struct GLNVGcontext { - GLNVGshader shader; - GLNVGtexture* textures; - float view[2]; - int ntextures; - int ctextures; - int textureId; - GLuint vertBuf; -#if defined NANOVG_GL3 - GLuint vertArr; -#endif -#if NANOVG_GL_USE_UNIFORMBUFFER - GLuint fragBuf; -#endif - int fragSize; - int flags; - - // Per frame buffers - GLNVGcall* calls; - int ccalls; - int ncalls; - GLNVGpath* paths; - int cpaths; - int npaths; - struct NVGvertex* verts; - int cverts; - int nverts; - unsigned char* uniforms; - int cuniforms; - int nuniforms; - - // cached state - #if NANOVG_GL_USE_STATE_FILTER - GLuint boundTexture; - GLuint stencilMask; - GLenum stencilFunc; - GLint stencilFuncRef; - GLuint stencilFuncMask; - GLNVGblend blendFunc; - #endif - - int dummyTex; -}; -typedef struct GLNVGcontext GLNVGcontext; - -static int glnvg__maxi(int a, int b) { return a > b ? a : b; } - -#ifdef NANOVG_GLES2 -static unsigned int glnvg__nearestPow2(unsigned int num) -{ - unsigned n = num > 0 ? num - 1 : 0; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n++; - return n; -} -#endif - -static void glnvg__bindTexture(GLNVGcontext* gl, GLuint tex) -{ -#if NANOVG_GL_USE_STATE_FILTER - if (gl->boundTexture != tex) { - gl->boundTexture = tex; - glBindTexture(GL_TEXTURE_2D, tex); - } -#else - glBindTexture(GL_TEXTURE_2D, tex); -#endif -} - -static void glnvg__stencilMask(GLNVGcontext* gl, GLuint mask) -{ -#if NANOVG_GL_USE_STATE_FILTER - if (gl->stencilMask != mask) { - gl->stencilMask = mask; - glStencilMask(mask); - } -#else - glStencilMask(mask); -#endif -} - -static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint mask) -{ -#if NANOVG_GL_USE_STATE_FILTER - if ((gl->stencilFunc != func) || - (gl->stencilFuncRef != ref) || - (gl->stencilFuncMask != mask)) { - - gl->stencilFunc = func; - gl->stencilFuncRef = ref; - gl->stencilFuncMask = mask; - glStencilFunc(func, ref, mask); - } -#else - glStencilFunc(func, ref, mask); -#endif -} -static void glnvg__blendFuncSeparate(GLNVGcontext* gl, const GLNVGblend* blend) -{ -#if NANOVG_GL_USE_STATE_FILTER - if ((gl->blendFunc.srcRGB != blend->srcRGB) || - (gl->blendFunc.dstRGB != blend->dstRGB) || - (gl->blendFunc.srcAlpha != blend->srcAlpha) || - (gl->blendFunc.dstAlpha != blend->dstAlpha)) { - - gl->blendFunc = *blend; - glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); - } -#else - glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); -#endif -} - -static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) -{ - GLNVGtexture* tex = NULL; - int i; - - for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].id == 0) { - tex = &gl->textures[i]; - break; - } - } - if (tex == NULL) { - if (gl->ntextures+1 > gl->ctextures) { - GLNVGtexture* textures; - int ctextures = glnvg__maxi(gl->ntextures+1, 4) + gl->ctextures/2; // 1.5x Overallocate - textures = (GLNVGtexture*)realloc(gl->textures, sizeof(GLNVGtexture)*ctextures); - if (textures == NULL) return NULL; - gl->textures = textures; - gl->ctextures = ctextures; - } - tex = &gl->textures[gl->ntextures++]; - } - - memset(tex, 0, sizeof(*tex)); - tex->id = ++gl->textureId; - - return tex; -} - -static GLNVGtexture* glnvg__findTexture(GLNVGcontext* gl, int id) -{ - int i; - for (i = 0; i < gl->ntextures; i++) - if (gl->textures[i].id == id) - return &gl->textures[i]; - return NULL; -} - -static int glnvg__deleteTexture(GLNVGcontext* gl, int id) -{ - int i; - for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].id == id) { - if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) - glDeleteTextures(1, &gl->textures[i].tex); - memset(&gl->textures[i], 0, sizeof(gl->textures[i])); - return 1; - } - } - return 0; -} - -static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) -{ - GLchar str[512+1]; - GLsizei len = 0; - glGetShaderInfoLog(shader, 512, &len, str); - if (len > 512) len = 512; - str[len] = '\0'; - printf("Shader %s/%s error:\n%s\n", name, type, str); -} - -static void glnvg__dumpProgramError(GLuint prog, const char* name) -{ - GLchar str[512+1]; - GLsizei len = 0; - glGetProgramInfoLog(prog, 512, &len, str); - if (len > 512) len = 512; - str[len] = '\0'; - printf("Program %s error:\n%s\n", name, str); -} - -static void glnvg__checkError(GLNVGcontext* gl, const char* str) -{ - GLenum err; - if ((gl->flags & NVG_DEBUG) == 0) return; - err = glGetError(); - if (err != GL_NO_ERROR) { - printf("Error %08x after %s\n", err, str); - return; - } -} - -static int glnvg__createShader(GLNVGshader* shader, const char* name, const char* header, const char* opts, const char* vshader, const char* fshader) -{ - GLint status; - GLuint prog, vert, frag; - const char* str[3]; - str[0] = header; - str[1] = opts != NULL ? opts : ""; - - memset(shader, 0, sizeof(*shader)); - - prog = glCreateProgram(); - vert = glCreateShader(GL_VERTEX_SHADER); - frag = glCreateShader(GL_FRAGMENT_SHADER); - str[2] = vshader; - glShaderSource(vert, 3, str, 0); - str[2] = fshader; - glShaderSource(frag, 3, str, 0); - - glCompileShader(vert); - glGetShaderiv(vert, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) { - glnvg__dumpShaderError(vert, name, "vert"); - return 0; - } - - glCompileShader(frag); - glGetShaderiv(frag, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) { - glnvg__dumpShaderError(frag, name, "frag"); - return 0; - } - - glAttachShader(prog, vert); - glAttachShader(prog, frag); - - glBindAttribLocation(prog, 0, "vertex"); - glBindAttribLocation(prog, 1, "tcoord"); - - glLinkProgram(prog); - glGetProgramiv(prog, GL_LINK_STATUS, &status); - if (status != GL_TRUE) { - glnvg__dumpProgramError(prog, name); - return 0; - } - - shader->prog = prog; - shader->vert = vert; - shader->frag = frag; - - return 1; -} - -static void glnvg__deleteShader(GLNVGshader* shader) -{ - if (shader->prog != 0) - glDeleteProgram(shader->prog); - if (shader->vert != 0) - glDeleteShader(shader->vert); - if (shader->frag != 0) - glDeleteShader(shader->frag); -} - -static void glnvg__getUniforms(GLNVGshader* shader) -{ - shader->loc[GLNVG_LOC_VIEWSIZE] = glGetUniformLocation(shader->prog, "viewSize"); - shader->loc[GLNVG_LOC_TEX] = glGetUniformLocation(shader->prog, "tex"); - -#if NANOVG_GL_USE_UNIFORMBUFFER - shader->loc[GLNVG_LOC_FRAG] = glGetUniformBlockIndex(shader->prog, "frag"); -#else - shader->loc[GLNVG_LOC_FRAG] = glGetUniformLocation(shader->prog, "frag"); -#endif -} - -static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); - -static int glnvg__renderCreate(void* uptr) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - int align = 4; - - // TODO: mediump float may not be enough for GLES2 in iOS. - // see the following discussion: https://github.com/memononen/nanovg/issues/46 - static const char* shaderHeader = -#if defined NANOVG_GL2 - "#define NANOVG_GL2 1\n" -#elif defined NANOVG_GL3 - "#version 150 core\n" - "#define NANOVG_GL3 1\n" -#elif defined NANOVG_GLES2 - "#version 100\n" - "#define NANOVG_GL2 1\n" -#elif defined NANOVG_GLES3 - "#version 300 es\n" - "#define NANOVG_GL3 1\n" -#endif - -#if NANOVG_GL_USE_UNIFORMBUFFER - "#define USE_UNIFORMBUFFER 1\n" -#else - "#define UNIFORMARRAY_SIZE 12\n" -#endif - "\n"; - - static const char* fillVertShader = - "#ifdef NANOVG_GL3\n" - " uniform vec2 viewSize;\n" - " in vec2 vertex;\n" - " in vec4 tcoord;\n" - " out vec2 ftcoord;\n" - " out vec2 fpos;\n" - " smooth out vec2 uv;\n" - "#else\n" - " uniform vec2 viewSize;\n" - " attribute vec2 vertex;\n" - " attribute vec4 tcoord;\n" - " varying vec2 ftcoord;\n" - " varying vec2 fpos;\n" - " varying vec2 uv;\n" - "#endif\n" - "void main(void) {\n" - " ftcoord = tcoord.xy;\n" - " uv = 0.5 * tcoord.zw;\n" - " fpos = vertex;\n" - " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" - "}\n"; - - static const char* fillFragShader = - "#ifdef GL_ES\n" - "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" - " precision highp float;\n" - "#else\n" - " precision mediump float;\n" - "#endif\n" - "#endif\n" - "#ifdef NANOVG_GL3\n" - "#ifdef USE_UNIFORMBUFFER\n" - " layout(std140) uniform frag {\n" - " mat3 scissorMat;\n" - " mat3 paintMat;\n" - " vec4 innerCol;\n" - " vec4 outerCol;\n" - " vec2 scissorExt;\n" - " vec2 scissorScale;\n" - " vec2 extent;\n" - " float radius;\n" - " float feather;\n" - " float strokeMult;\n" - " float strokeThr;\n" - " int lineStyle;\n" - " int texType;\n" - " int type;\n" - " };\n" - "#else\n" // NANOVG_GL3 && !USE_UNIFORMBUFFER - " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" - "#endif\n" - " uniform sampler2D tex;\n" - " in vec2 ftcoord;\n" - " in vec2 fpos;\n" - " smooth in vec2 uv;\n" - " out vec4 outColor;\n" - "#else\n" // !NANOVG_GL3 - " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" - " uniform sampler2D tex;\n" - " varying vec2 ftcoord;\n" - " varying vec2 fpos;\n" - " varying vec2 uv;\n" - "#endif\n" - "#ifndef USE_UNIFORMBUFFER\n" - " #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)\n" - " #define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz)\n" - " #define innerCol frag[6]\n" - " #define outerCol frag[7]\n" - " #define scissorExt frag[8].xy\n" - " #define scissorScale frag[8].zw\n" - " #define extent frag[9].xy\n" - " #define radius frag[9].z\n" - " #define feather frag[9].w\n" - " #define strokeMult frag[10].x\n" - " #define strokeThr frag[10].y\n" - " #define lineStyle int(frag[10].z)\n" - " #define texType int(frag[10].w)\n" - " #define type int(frag[11].x)\n" - "#endif\n" - "\n" - "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" - " vec2 ext2 = ext - vec2(rad,rad);\n" - " vec2 d = abs(pt) - ext2;\n" - " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" - "}\n" - "\n" - "// Scissoring\n" - "float scissorMask(vec2 p) {\n" - " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" - " sc = vec2(0.5,0.5) - sc * scissorScale;\n" - " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" - "}\n" - "float glow(vec2 uv){\n" - " return smoothstep(0.0, 1.0, 1.0 - 2.0 * abs(uv.x));\n" - "}\n" - "float dashed(vec2 uv){\n" - " float fy = fract(uv.y / 4.0);\n" - " float w = step(fy, 0.5);\n" - " fy *= 4.0;\n" - " if(fy >= 1.5){\n" - " fy -= 1.5;\n" - " } else if(fy <= 0.5) {\n" - " fy = 0.5 - fy;\n" - "} else {\n" - " fy = 0.0;\n" - "}\n" - "w *= smoothstep(0.0, 1.0, 6.0 * (0.25 - (uv.x * uv.x + fy * fy)));\n" - " return w;\n" - "}\n" - "float dotted(vec2 uv){\n" - " float fy = 4.0 * fract(uv.y / (4.0)) - 0.5;\n" - " return smoothstep(0.0, 1.0, 6.0 * (0.25 - (uv.x * uv.x + fy * fy)));\n" - "}\n" - "#ifdef EDGE_AA\n" - "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" - "float strokeMask() {\n" - " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);\n" - "}\n" - "#endif\n" - "\n" - "void main(void) {\n" - " vec4 result;\n" - " float scissor = scissorMask(fpos);\n" - "#ifdef EDGE_AA\n" - " float strokeAlpha = strokeMask();\n" - "if(lineStyle == 2) strokeAlpha*=dashed(uv);\n" - "if(lineStyle == 3) strokeAlpha*=dotted(uv);\n" - "if(lineStyle == 4) strokeAlpha*=glow(uv);\n" - " if (strokeAlpha < strokeThr) discard;\n" - "#else\n" - " float strokeAlpha = 1.0;\n" - "if(lineStyle == 2) strokeAlpha*=dashed(uv);\n" - "if(lineStyle == 3) strokeAlpha*=dotted(uv);\n" - "if(lineStyle == 4) strokeAlpha*=glow(uv);\n" - "if (lineStyle > 1 && strokeAlpha < strokeThr) discard;\n" - "#endif\n" - " if (type == 0) { // Gradient\n" - " // Calculate gradient color using box gradient\n" - " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" - " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" - " vec4 color = mix(innerCol,outerCol,d);\n" - " // Combine alpha\n" - " color *= strokeAlpha * scissor;\n" - " result = color;\n" - " } else if (type == 1) { // Image\n" - " // Calculate color fron texture\n" - " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" - "#ifdef NANOVG_GL3\n" - " vec4 color = texture(tex, pt);\n" - "#else\n" - " vec4 color = texture2D(tex, pt);\n" - "#endif\n" - " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" - " if (texType == 2) color = vec4(color.x);" - " // Apply color tint and alpha.\n" - " color *= innerCol;\n" - " // Combine alpha\n" - " color *= strokeAlpha * scissor;\n" - " result = color;\n" - " } else if (type == 2) { // Stencil fill\n" - " result = vec4(1,1,1,1);\n" - " } else if (type == 3) { // Textured tris\n" - "#ifdef NANOVG_GL3\n" - " vec4 color = texture(tex, ftcoord);\n" - "#else\n" - " vec4 color = texture2D(tex, ftcoord);\n" - "#endif\n" - " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" - " if (texType == 2) color = vec4(color.x);" - " color *= scissor;\n" - " result = color * innerCol;\n" - " }\n" - "#ifdef NANOVG_GL3\n" - " outColor = result;\n" - "#else\n" - " gl_FragColor = result;\n" - "#endif\n" - "}\n"; - - glnvg__checkError(gl, "init"); - - if (gl->flags & NVG_ANTIALIAS) { - if (glnvg__createShader(&gl->shader, "shader", shaderHeader, "#define EDGE_AA 1\n", fillVertShader, fillFragShader) == 0) - return 0; - } else { - if (glnvg__createShader(&gl->shader, "shader", shaderHeader, NULL, fillVertShader, fillFragShader) == 0) - return 0; - } - - glnvg__checkError(gl, "uniform locations"); - glnvg__getUniforms(&gl->shader); - - // Create dynamic vertex array -#if defined NANOVG_GL3 - glGenVertexArrays(1, &gl->vertArr); -#endif - glGenBuffers(1, &gl->vertBuf); - -#if NANOVG_GL_USE_UNIFORMBUFFER - // Create UBOs - glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); - glGenBuffers(1, &gl->fragBuf); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); -#endif - gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; - - // Some platforms does not allow to have samples to unset textures. - // Create empty one which is bound when there's no texture specified. - gl->dummyTex = glnvg__renderCreateTexture(gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); - - glnvg__checkError(gl, "create done"); - - glFinish(); - - return 1; -} - -static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGtexture* tex = glnvg__allocTexture(gl); - - if (tex == NULL) return 0; - -#ifdef NANOVG_GLES2 - // Check for non-power of 2. - if (glnvg__nearestPow2(w) != (unsigned int)w || glnvg__nearestPow2(h) != (unsigned int)h) { - // No repeat - if ((imageFlags & NVG_IMAGE_REPEATX) != 0 || (imageFlags & NVG_IMAGE_REPEATY) != 0) { - printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); - imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); - } - // No mips. - if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { - printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); - imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; - } - } -#endif - - glGenTextures(1, &tex->tex); - tex->width = w; - tex->height = h; - tex->type = type; - tex->flags = imageFlags; - glnvg__bindTexture(gl, tex->tex); - - glPixelStorei(GL_UNPACK_ALIGNMENT,1); -#ifndef NANOVG_GLES2 - glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); -#endif - -#if defined (NANOVG_GL2) - // GL 1.4 and later has support for generating mipmaps using a tex parameter. - if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); - } -#endif - - if (type == NVG_TEXTURE_RGBA) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - else -#if defined(NANOVG_GLES2) || defined (NANOVG_GL2) - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); -#elif defined(NANOVG_GLES3) - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); -#else - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); -#endif - - if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { - if (imageFlags & NVG_IMAGE_NEAREST) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - } else { - if (imageFlags & NVG_IMAGE_NEAREST) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - } - - if (imageFlags & NVG_IMAGE_NEAREST) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - - if (imageFlags & NVG_IMAGE_REPEATX) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - - if (imageFlags & NVG_IMAGE_REPEATY) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); -#ifndef NANOVG_GLES2 - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); -#endif - - // The new way to build mipmaps on GLES and GL3 -#if !defined(NANOVG_GL2) - if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { - glGenerateMipmap(GL_TEXTURE_2D); - } -#endif - - glnvg__checkError(gl, "create tex"); - glnvg__bindTexture(gl, 0); - - return tex->id; -} - - -static int glnvg__renderDeleteTexture(void* uptr, int image) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - return glnvg__deleteTexture(gl, image); -} - -static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGtexture* tex = glnvg__findTexture(gl, image); - - if (tex == NULL) return 0; - glnvg__bindTexture(gl, tex->tex); - - glPixelStorei(GL_UNPACK_ALIGNMENT,1); - -#ifndef NANOVG_GLES2 - glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); - glPixelStorei(GL_UNPACK_SKIP_ROWS, y); -#else - // No support for all of skip, need to update a whole row at a time. - if (tex->type == NVG_TEXTURE_RGBA) - data += y*tex->width*4; - else - data += y*tex->width; - x = 0; - w = tex->width; -#endif - - if (tex->type == NVG_TEXTURE_RGBA) - glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); - else -#if defined(NANOVG_GLES2) || defined(NANOVG_GL2) - glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); -#else - glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); -#endif - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); -#ifndef NANOVG_GLES2 - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); -#endif - - glnvg__bindTexture(gl, 0); - - return 1; -} - -static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGtexture* tex = glnvg__findTexture(gl, image); - if (tex == NULL) return 0; - *w = tex->width; - *h = tex->height; - return 1; -} - -static int glnvg__renderGetImageTextureId(void* uptr, int handle) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGtexture* tex = glnvg__findTexture(gl, handle); - if(tex) { - return tex->tex; - } else { - return -1; - } -} - -static void glnvg__xformToMat3x4(float* m3, float* t) -{ - m3[0] = t[0]; - m3[1] = t[1]; - m3[2] = 0.0f; - m3[3] = 0.0f; - m3[4] = t[2]; - m3[5] = t[3]; - m3[6] = 0.0f; - m3[7] = 0.0f; - m3[8] = t[4]; - m3[9] = t[5]; - m3[10] = 1.0f; - m3[11] = 0.0f; -} - -static NVGcolor glnvg__premulColor(NVGcolor c) -{ - c.r *= c.a; - c.g *= c.a; - c.b *= c.a; - return c; -} - -static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpaint* paint, - NVGscissor* scissor, float width, float fringe, float strokeThr, int lineStyle) -{ - GLNVGtexture* tex = NULL; - float invxform[6]; - - memset(frag, 0, sizeof(*frag)); - - frag->innerCol = glnvg__premulColor(paint->innerColor); - frag->outerCol = glnvg__premulColor(paint->outerColor); - frag->lineStyle = lineStyle; - if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) { - memset(frag->scissorMat, 0, sizeof(frag->scissorMat)); - frag->scissorExt[0] = 1.0f; - frag->scissorExt[1] = 1.0f; - frag->scissorScale[0] = 1.0f; - frag->scissorScale[1] = 1.0f; - } else { - nvgTransformInverse(invxform, scissor->xform); - glnvg__xformToMat3x4(frag->scissorMat, invxform); - frag->scissorExt[0] = scissor->extent[0]; - frag->scissorExt[1] = scissor->extent[1]; - frag->scissorScale[0] = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe; - frag->scissorScale[1] = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe; - } - - memcpy(frag->extent, paint->extent, sizeof(frag->extent)); - frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe; - frag->strokeThr = strokeThr; - - if (paint->image != 0) { - tex = glnvg__findTexture(gl, paint->image); - if (tex == NULL) return 0; - if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { - float m1[6], m2[6]; - nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f); - nvgTransformMultiply(m1, paint->xform); - nvgTransformScale(m2, 1.0f, -1.0f); - nvgTransformMultiply(m2, m1); - nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f); - nvgTransformMultiply(m1, m2); - nvgTransformInverse(invxform, m1); - } else { - nvgTransformInverse(invxform, paint->xform); - } - frag->type = NSVG_SHADER_FILLIMG; - - #if NANOVG_GL_USE_UNIFORMBUFFER - if (tex->type == NVG_TEXTURE_RGBA) - frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; - else - frag->texType = 2; - #else - if (tex->type == NVG_TEXTURE_RGBA) - frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; - else - frag->texType = 2.0f; - #endif -// printf("frag->texType = %d\n", frag->texType); - } else { - frag->type = NSVG_SHADER_FILLGRAD; - frag->radius = paint->radius; - frag->feather = paint->feather; - nvgTransformInverse(invxform, paint->xform); - } - - glnvg__xformToMat3x4(frag->paintMat, invxform); - - return 1; -} - -static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i); - -static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) -{ - GLNVGtexture* tex = NULL; -#if NANOVG_GL_USE_UNIFORMBUFFER - glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); -#else - GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset); - glUniform4fv(gl->shader.loc[GLNVG_LOC_FRAG], NANOVG_GL_UNIFORMARRAY_SIZE, &(frag->uniformArray[0][0])); -#endif - - if (image != 0) { - tex = glnvg__findTexture(gl, image); - } - // If no image is set, use empty texture - if (tex == NULL) { - tex = glnvg__findTexture(gl, gl->dummyTex); - } - glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); - glnvg__checkError(gl, "tex paint tex"); -} - -static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) -{ - NVG_NOTUSED(devicePixelRatio); - GLNVGcontext* gl = (GLNVGcontext*)uptr; - gl->view[0] = width; - gl->view[1] = height; -} - -static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) -{ - GLNVGpath* paths = &gl->paths[call->pathOffset]; - int i, npaths = call->pathCount; - - // Draw shapes - glEnable(GL_STENCIL_TEST); - glnvg__stencilMask(gl, 0xff); - glnvg__stencilFunc(gl, GL_ALWAYS, 0, 0xff); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - - // set bindpoint for solid loc - glnvg__setUniforms(gl, call->uniformOffset, 0); - glnvg__checkError(gl, "fill simple"); - - glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); - glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); - glDisable(GL_CULL_FACE); - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); - glEnable(GL_CULL_FACE); - - // Draw anti-aliased pixels - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); - glnvg__checkError(gl, "fill fill"); - - if (gl->flags & NVG_ANTIALIAS) { - glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - // Draw fringes - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - } - - // Draw fill - glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); - glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); - glDrawArrays(GL_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount); - - glDisable(GL_STENCIL_TEST); -} - -static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call) -{ - GLNVGpath* paths = &gl->paths[call->pathOffset]; - int i, npaths = call->pathCount; - - glnvg__setUniforms(gl, call->uniformOffset, call->image); - glnvg__checkError(gl, "convex fill"); - - for (i = 0; i < npaths; i++) { - glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); - // Draw fringes - if (paths[i].strokeCount > 0) { - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - } - } -} - -static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call) -{ - GLNVGpath* paths = &gl->paths[call->pathOffset]; - int npaths = call->pathCount, i; - - if (gl->flags & NVG_STENCIL_STROKES) { - - glEnable(GL_STENCIL_TEST); - glnvg__stencilMask(gl, 0xff); - - // Fill the stroke base without overlap - glnvg__stencilFunc(gl, GL_EQUAL, 0x0, 0xff); - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); - glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); - glnvg__checkError(gl, "stroke fill 0"); - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - - // Draw anti-aliased pixels. - glnvg__setUniforms(gl, call->uniformOffset, call->image); - glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - - // Clear stencil buffer. - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); - glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); - glnvg__checkError(gl, "stroke fill 1"); - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - glDisable(GL_STENCIL_TEST); - -// glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); - - } else { - glnvg__setUniforms(gl, call->uniformOffset, call->image); - glnvg__checkError(gl, "stroke fill"); - // Draw Strokes - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - } -} - -static void glnvg__triangles(GLNVGcontext* gl, GLNVGcall* call) -{ - glnvg__setUniforms(gl, call->uniformOffset, call->image); - glnvg__checkError(gl, "triangles fill"); - - glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); -} - -static void glnvg__renderCancel(void* uptr) { - GLNVGcontext* gl = (GLNVGcontext*)uptr; - gl->nverts = 0; - gl->npaths = 0; - gl->ncalls = 0; - gl->nuniforms = 0; -} - -static GLenum glnvg_convertBlendFuncFactor(int factor) -{ - if (factor == NVG_ZERO) - return GL_ZERO; - if (factor == NVG_ONE) - return GL_ONE; - if (factor == NVG_SRC_COLOR) - return GL_SRC_COLOR; - if (factor == NVG_ONE_MINUS_SRC_COLOR) - return GL_ONE_MINUS_SRC_COLOR; - if (factor == NVG_DST_COLOR) - return GL_DST_COLOR; - if (factor == NVG_ONE_MINUS_DST_COLOR) - return GL_ONE_MINUS_DST_COLOR; - if (factor == NVG_SRC_ALPHA) - return GL_SRC_ALPHA; - if (factor == NVG_ONE_MINUS_SRC_ALPHA) - return GL_ONE_MINUS_SRC_ALPHA; - if (factor == NVG_DST_ALPHA) - return GL_DST_ALPHA; - if (factor == NVG_ONE_MINUS_DST_ALPHA) - return GL_ONE_MINUS_DST_ALPHA; - if (factor == NVG_SRC_ALPHA_SATURATE) - return GL_SRC_ALPHA_SATURATE; - return GL_INVALID_ENUM; -} - -static GLNVGblend glnvg__blendCompositeOperation(NVGcompositeOperationState op) -{ - GLNVGblend blend; - blend.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB); - blend.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB); - blend.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha); - blend.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha); - if (blend.srcRGB == GL_INVALID_ENUM || blend.dstRGB == GL_INVALID_ENUM || blend.srcAlpha == GL_INVALID_ENUM || blend.dstAlpha == GL_INVALID_ENUM) - { - blend.srcRGB = GL_ONE; - blend.dstRGB = GL_ONE_MINUS_SRC_ALPHA; - blend.srcAlpha = GL_ONE; - blend.dstAlpha = GL_ONE_MINUS_SRC_ALPHA; - } - return blend; -} - -static void glnvg__renderFlush(void* uptr) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - int i; - - if (gl->ncalls > 0) { - - // Setup require GL state. - glUseProgram(gl->shader.prog); - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glFrontFace(GL_CCW); - glEnable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glStencilMask(0xffffffff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilFunc(GL_ALWAYS, 0, 0xffffffff); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - #if NANOVG_GL_USE_STATE_FILTER - gl->boundTexture = 0; - gl->stencilMask = 0xffffffff; - gl->stencilFunc = GL_ALWAYS; - gl->stencilFuncRef = 0; - gl->stencilFuncMask = 0xffffffff; - gl->blendFunc.srcRGB = GL_INVALID_ENUM; - gl->blendFunc.srcAlpha = GL_INVALID_ENUM; - gl->blendFunc.dstRGB = GL_INVALID_ENUM; - gl->blendFunc.dstAlpha = GL_INVALID_ENUM; - #endif - -#if NANOVG_GL_USE_UNIFORMBUFFER - // Upload ubo for frag shaders - glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); - glBufferData(GL_UNIFORM_BUFFER, gl->nuniforms * gl->fragSize, gl->uniforms, GL_STREAM_DRAW); -#endif - - // Upload vertex data -#if defined NANOVG_GL3 - glBindVertexArray(gl->vertArr); -#endif - glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); - glBufferData(GL_ARRAY_BUFFER, gl->nverts * sizeof(NVGvertex), gl->verts, GL_STREAM_DRAW); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(size_t)0); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(0 + 2*sizeof(float))); - - // Set view and texture just once per frame. - glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); - glUniform2fv(gl->shader.loc[GLNVG_LOC_VIEWSIZE], 1, gl->view); - -#if NANOVG_GL_USE_UNIFORMBUFFER - glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); -#endif - - for (i = 0; i < gl->ncalls; i++) { - GLNVGcall* call = &gl->calls[i]; - glnvg__blendFuncSeparate(gl,&call->blendFunc); - if (call->type == GLNVG_FILL) - glnvg__fill(gl, call); - else if (call->type == GLNVG_CONVEXFILL) - glnvg__convexFill(gl, call); - else if (call->type == GLNVG_STROKE) - glnvg__stroke(gl, call); - else if (call->type == GLNVG_TRIANGLES) - glnvg__triangles(gl, call); - } - - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); -#if defined NANOVG_GL3 - glBindVertexArray(0); -#endif - glDisable(GL_CULL_FACE); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glUseProgram(0); - glnvg__bindTexture(gl, 0); - } - - // Reset calls - gl->nverts = 0; - gl->npaths = 0; - gl->ncalls = 0; - gl->nuniforms = 0; -} - -static int glnvg__maxVertCount(const NVGpath* paths, int npaths) -{ - int i, count = 0; - for (i = 0; i < npaths; i++) { - count += paths[i].nfill; - count += paths[i].nstroke; - } - return count; -} - -static GLNVGcall* glnvg__allocCall(GLNVGcontext* gl) -{ - GLNVGcall* ret = NULL; - if (gl->ncalls+1 > gl->ccalls) { - GLNVGcall* calls; - int ccalls = glnvg__maxi(gl->ncalls+1, 128) + gl->ccalls/2; // 1.5x Overallocate - calls = (GLNVGcall*)realloc(gl->calls, sizeof(GLNVGcall) * ccalls); - if (calls == NULL) return NULL; - gl->calls = calls; - gl->ccalls = ccalls; - } - ret = &gl->calls[gl->ncalls++]; - memset(ret, 0, sizeof(GLNVGcall)); - return ret; -} - -static int glnvg__allocPaths(GLNVGcontext* gl, int n) -{ - int ret = 0; - if (gl->npaths+n > gl->cpaths) { - GLNVGpath* paths; - int cpaths = glnvg__maxi(gl->npaths + n, 128) + gl->cpaths/2; // 1.5x Overallocate - paths = (GLNVGpath*)realloc(gl->paths, sizeof(GLNVGpath) * cpaths); - if (paths == NULL) return -1; - gl->paths = paths; - gl->cpaths = cpaths; - } - ret = gl->npaths; - gl->npaths += n; - return ret; -} - -static int glnvg__allocVerts(GLNVGcontext* gl, int n) -{ - int ret = 0; - if (gl->nverts+n > gl->cverts) { - NVGvertex* verts; - int cverts = glnvg__maxi(gl->nverts + n, 4096) + gl->cverts/2; // 1.5x Overallocate - verts = (NVGvertex*)realloc(gl->verts, sizeof(NVGvertex) * cverts); - if (verts == NULL) return -1; - gl->verts = verts; - gl->cverts = cverts; - } - ret = gl->nverts; - gl->nverts += n; - return ret; -} - -static int glnvg__allocFragUniforms(GLNVGcontext* gl, int n) -{ - int ret = 0, structSize = gl->fragSize; - if (gl->nuniforms+n > gl->cuniforms) { - unsigned char* uniforms; - int cuniforms = glnvg__maxi(gl->nuniforms+n, 128) + gl->cuniforms/2; // 1.5x Overallocate - uniforms = (unsigned char*)realloc(gl->uniforms, structSize * cuniforms); - if (uniforms == NULL) return -1; - gl->uniforms = uniforms; - gl->cuniforms = cuniforms; - } - ret = gl->nuniforms * structSize; - gl->nuniforms += n; - return ret; -} - -static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i) -{ - return (GLNVGfragUniforms*)&gl->uniforms[i]; -} - -static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v) -{ - vtx->x = x; - vtx->y = y; - vtx->u = u; - vtx->v = v; -} - -static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, - const float* bounds, const NVGpath* paths, int npaths) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGcall* call = glnvg__allocCall(gl); - NVGvertex* quad; - GLNVGfragUniforms* frag; - int i, maxverts, offset; - - if (call == NULL) return; - - call->type = GLNVG_FILL; - call->triangleCount = 4; - call->pathOffset = glnvg__allocPaths(gl, npaths); - if (call->pathOffset == -1) goto error; - call->pathCount = npaths; - call->image = paint->image; - call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); - - if (npaths == 1 && paths[0].convex) - { - call->type = GLNVG_CONVEXFILL; - call->triangleCount = 0; // Bounding box fill quad not needed for convex fill - } - - // Allocate vertices for all the paths. - maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount; - offset = glnvg__allocVerts(gl, maxverts); - if (offset == -1) goto error; - - for (i = 0; i < npaths; i++) { - GLNVGpath* copy = &gl->paths[call->pathOffset + i]; - const NVGpath* path = &paths[i]; - memset(copy, 0, sizeof(GLNVGpath)); - if (path->nfill > 0) { - copy->fillOffset = offset; - copy->fillCount = path->nfill; - memcpy(&gl->verts[offset], path->fill, sizeof(NVGvertex) * path->nfill); - offset += path->nfill; - } - if (path->nstroke > 0) { - copy->strokeOffset = offset; - copy->strokeCount = path->nstroke; - memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); - offset += path->nstroke; - } - } - - // Setup uniforms for draw calls - if (call->type == GLNVG_FILL) { - // Quad - call->triangleOffset = offset; - quad = &gl->verts[call->triangleOffset]; - glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); - glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); - glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); - glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); - - call->uniformOffset = glnvg__allocFragUniforms(gl, 2); - if (call->uniformOffset == -1) goto error; - // Simple shader for stencil - frag = nvg__fragUniformPtr(gl, call->uniformOffset); - memset(frag, 0, sizeof(*frag)); - frag->strokeThr = -1.0f; - frag->type = NSVG_SHADER_SIMPLE; - // Fill shader - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe, -1.0f, 0); - } else { - call->uniformOffset = glnvg__allocFragUniforms(gl, 1); - if (call->uniformOffset == -1) goto error; - // Fill shader - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f, 0); - } - - return; - -error: - // We get here if call alloc was ok, but something else is not. - // Roll back the last call to prevent drawing it. - if (gl->ncalls > 0) gl->ncalls--; -} - -static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, - float strokeWidth, int lineStyle, const NVGpath* paths, int npaths) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGcall* call = glnvg__allocCall(gl); - int i, maxverts, offset; - - if (call == NULL) return; - - call->type = GLNVG_STROKE; - call->pathOffset = glnvg__allocPaths(gl, npaths); - if (call->pathOffset == -1) goto error; - call->pathCount = npaths; - call->image = paint->image; - call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); - - // Allocate vertices for all the paths. - maxverts = glnvg__maxVertCount(paths, npaths); - offset = glnvg__allocVerts(gl, maxverts); - if (offset == -1) goto error; - - for (i = 0; i < npaths; i++) { - GLNVGpath* copy = &gl->paths[call->pathOffset + i]; - const NVGpath* path = &paths[i]; - memset(copy, 0, sizeof(GLNVGpath)); - if (path->nstroke) { - copy->strokeOffset = offset; - copy->strokeCount = path->nstroke; - memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); - offset += path->nstroke; - } - } - - if (gl->flags & NVG_STENCIL_STROKES) { - // Fill shader - call->uniformOffset = glnvg__allocFragUniforms(gl, 2); - if (call->uniformOffset == -1) goto error; - - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f, lineStyle); - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f, lineStyle); - - } else { - // Fill shader - call->uniformOffset = glnvg__allocFragUniforms(gl, 1); - if (call->uniformOffset == -1) goto error; - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f, lineStyle); - } - - return; - -error: - // We get here if call alloc was ok, but something else is not. - // Roll back the last call to prevent drawing it. - if (gl->ncalls > 0) gl->ncalls--; -} - -static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, - const NVGvertex* verts, int nverts, float fringe) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGcall* call = glnvg__allocCall(gl); - GLNVGfragUniforms* frag; - - if (call == NULL) return; - - call->type = GLNVG_TRIANGLES; - call->image = paint->image; - call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); - - // Allocate vertices for all the paths. - call->triangleOffset = glnvg__allocVerts(gl, nverts); - if (call->triangleOffset == -1) goto error; - call->triangleCount = nverts; - - memcpy(&gl->verts[call->triangleOffset], verts, sizeof(NVGvertex) * nverts); - - // Fill shader - call->uniformOffset = glnvg__allocFragUniforms(gl, 1); - if (call->uniformOffset == -1) goto error; - frag = nvg__fragUniformPtr(gl, call->uniformOffset); - glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f, 0); - frag->type = NSVG_SHADER_IMG; - - return; - -error: - // We get here if call alloc was ok, but something else is not. - // Roll back the last call to prevent drawing it. - if (gl->ncalls > 0) gl->ncalls--; -} - -static void glnvg__renderDelete(void* uptr) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - int i; - if (gl == NULL) return; - - glnvg__deleteShader(&gl->shader); - -#if NANOVG_GL3 -#if NANOVG_GL_USE_UNIFORMBUFFER - if (gl->fragBuf != 0) - glDeleteBuffers(1, &gl->fragBuf); -#endif - if (gl->vertArr != 0) - glDeleteVertexArrays(1, &gl->vertArr); -#endif - if (gl->vertBuf != 0) - glDeleteBuffers(1, &gl->vertBuf); - - for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) - glDeleteTextures(1, &gl->textures[i].tex); - } - free(gl->textures); - - free(gl->paths); - free(gl->verts); - free(gl->uniforms); - free(gl->calls); - - free(gl); -} - - -#if defined NANOVG_GL2 -NVGcontext* nvgCreateGL2(int flags) -#elif defined NANOVG_GL3 -NVGcontext* nvgCreateGL3(int flags) -#elif defined NANOVG_GLES2 -NVGcontext* nvgCreateGLES2(int flags) -#elif defined NANOVG_GLES3 -NVGcontext* nvgCreateGLES3(int flags) -#endif -{ - NVGparams params; - NVGcontext* ctx = NULL; - GLNVGcontext* gl = (GLNVGcontext*)malloc(sizeof(GLNVGcontext)); - if (gl == NULL) goto error; - memset(gl, 0, sizeof(GLNVGcontext)); - - memset(¶ms, 0, sizeof(params)); - params.renderCreate = glnvg__renderCreate; - params.renderCreateTexture = glnvg__renderCreateTexture; - params.renderDeleteTexture = glnvg__renderDeleteTexture; - params.renderUpdateTexture = glnvg__renderUpdateTexture; - params.renderGetTextureSize = glnvg__renderGetTextureSize; - params.renderGetImageTextureId = glnvg__renderGetImageTextureId; - params.renderViewport = glnvg__renderViewport; - params.renderCancel = glnvg__renderCancel; - params.renderFlush = glnvg__renderFlush; - params.renderFill = glnvg__renderFill; - params.renderStroke = glnvg__renderStroke; - params.renderTriangles = glnvg__renderTriangles; - params.renderDelete = glnvg__renderDelete; - params.userPtr = gl; - params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0; - - gl->flags = flags; - - ctx = nvgCreateInternal(¶ms); - if (ctx == NULL) goto error; - - return ctx; - -error: - // 'gl' is freed by nvgDeleteInternal. - if (ctx != NULL) nvgDeleteInternal(ctx); - return NULL; -} - -#if defined NANOVG_GL2 -void nvgDeleteGL2(NVGcontext* ctx) -#elif defined NANOVG_GL3 -void nvgDeleteGL3(NVGcontext* ctx) -#elif defined NANOVG_GLES2 -void nvgDeleteGLES2(NVGcontext* ctx) -#elif defined NANOVG_GLES3 -void nvgDeleteGLES3(NVGcontext* ctx) -#endif -{ - nvgDeleteInternal(ctx); -} - -#if defined NANOVG_GL2 -int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) -#elif defined NANOVG_GL3 -int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) -#elif defined NANOVG_GLES2 -int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) -#elif defined NANOVG_GLES3 -int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) -#endif -{ - GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; - GLNVGtexture* tex = glnvg__allocTexture(gl); - - if (tex == NULL) return 0; - - tex->type = NVG_TEXTURE_RGBA; - tex->tex = textureId; - tex->flags = imageFlags; - tex->width = w; - tex->height = h; - - return tex->id; -} - -#if defined NANOVG_GL2 -GLuint nvglImageHandleGL2(NVGcontext* ctx, int image) -#elif defined NANOVG_GL3 -GLuint nvglImageHandleGL3(NVGcontext* ctx, int image) -#elif defined NANOVG_GLES2 -GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image) -#elif defined NANOVG_GLES3 -GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image) -#endif -{ - GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; - GLNVGtexture* tex = glnvg__findTexture(gl, image); - return tex->tex; -} - -#endif /* NANOVG_GL_IMPLEMENTATION */ diff --git a/src/nanovg_gl_utils.h b/src/nanovg_gl_utils.h deleted file mode 100644 index fc6e98b..0000000 --- a/src/nanovg_gl_utils.h +++ /dev/null @@ -1,154 +0,0 @@ -// -// Copyright (c) 2009-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. -// -#ifndef NANOVG_GL_UTILS_H -#define NANOVG_GL_UTILS_H - -struct NVGLUframebuffer { - NVGcontext* ctx; - GLuint fbo; - GLuint rbo; - GLuint texture; - int image; -}; -typedef struct NVGLUframebuffer NVGLUframebuffer; - -// Helper function to create GL frame buffer to render to. -void nvgluBindFramebuffer(NVGLUframebuffer* fb); -NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); -void nvgluDeleteFramebuffer(NVGLUframebuffer* fb); - -#endif // NANOVG_GL_UTILS_H - -#ifdef NANOVG_GL_IMPLEMENTATION - -#if defined(NANOVG_GL3) || defined(NANOVG_GLES2) || defined(NANOVG_GLES3) -// FBO is core in OpenGL 3>. -# define NANOVG_FBO_VALID 1 -#elif defined(NANOVG_GL2) -// On OS X including glext defines FBO on GL2 too. -# ifdef __APPLE__ -# include -# define NANOVG_FBO_VALID 1 -# endif -#endif - -static GLint defaultFBO = -1; - -NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags) -{ -#ifdef NANOVG_FBO_VALID - GLint defaultFBO; - GLint defaultRBO; - NVGLUframebuffer* fb = NULL; - - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); - glGetIntegerv(GL_RENDERBUFFER_BINDING, &defaultRBO); - - fb = (NVGLUframebuffer*)malloc(sizeof(NVGLUframebuffer)); - if (fb == NULL) goto error; - memset(fb, 0, sizeof(NVGLUframebuffer)); - - fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); - -#if defined NANOVG_GL2 - fb->texture = nvglImageHandleGL2(ctx, fb->image); -#elif defined NANOVG_GL3 - fb->texture = nvglImageHandleGL3(ctx, fb->image); -#elif defined NANOVG_GLES2 - fb->texture = nvglImageHandleGLES2(ctx, fb->image); -#elif defined NANOVG_GLES3 - fb->texture = nvglImageHandleGLES3(ctx, fb->image); -#endif - - fb->ctx = ctx; - - // frame buffer object - glGenFramebuffers(1, &fb->fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo); - - // render buffer object - glGenRenderbuffers(1, &fb->rbo); - glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo); - glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h); - - // combine all - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { -#ifdef GL_DEPTH24_STENCIL8 - // If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback. - // Some graphics cards require a depth buffer along with a stencil. - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) -#endif // GL_DEPTH24_STENCIL8 - goto error; - } - - glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); - glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); - return fb; -error: - glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); - glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); - nvgluDeleteFramebuffer(fb); - return NULL; -#else - NVG_NOTUSED(ctx); - NVG_NOTUSED(w); - NVG_NOTUSED(h); - NVG_NOTUSED(imageFlags); - return NULL; -#endif -} - -void nvgluBindFramebuffer(NVGLUframebuffer* fb) -{ -#ifdef NANOVG_FBO_VALID - if (defaultFBO == -1) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); - glBindFramebuffer(GL_FRAMEBUFFER, fb != NULL ? fb->fbo : (GLuint)defaultFBO); -#else - NVG_NOTUSED(fb); -#endif -} - -void nvgluDeleteFramebuffer(NVGLUframebuffer* fb) -{ -#ifdef NANOVG_FBO_VALID - if (fb == NULL) return; - if (fb->fbo != 0) - glDeleteFramebuffers(1, &fb->fbo); - if (fb->rbo != 0) - glDeleteRenderbuffers(1, &fb->rbo); - if (fb->image >= 0) - nvgDeleteImage(fb->ctx, fb->image); - fb->ctx = NULL; - fb->fbo = 0; - fb->rbo = 0; - fb->texture = 0; - fb->image = -1; - free(fb); -#else - NVG_NOTUSED(fb); -#endif -} - -#endif // NANOVG_GL_IMPLEMENTATION