├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── example ├── LICENSE_OFL.txt ├── NotoEmoji-Regular.ttf ├── Roboto-Bold.ttf ├── Roboto-Light.ttf ├── Roboto-Regular.ttf ├── demo.c ├── demo.h ├── entypo.ttf ├── example_fbo.c ├── example_gl2.c ├── example_gl3.c ├── example_gles2.c ├── example_gles3.c ├── images.txt ├── images │ ├── image1.jpg │ ├── image10.jpg │ ├── image11.jpg │ ├── image12.jpg │ ├── image2.jpg │ ├── image3.jpg │ ├── image4.jpg │ ├── image5.jpg │ ├── image6.jpg │ ├── image7.jpg │ ├── image8.jpg │ └── image9.jpg ├── perf.c ├── perf.h ├── screenshot-01.png ├── screenshot-02.png └── stb_image_write.h ├── obsolete ├── nanovg_gl2.h ├── nanovg_gl3.h └── obsolete.md ├── premake4.lua └── src ├── fontstash.h ├── nanovg.c ├── nanovg.h ├── nanovg_gl.h ├── nanovg_gl_utils.h ├── stb_image.h └── stb_truetype.h /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | env: 6 | BUILD_TYPE: release 7 | 8 | jobs: 9 | build: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | os: [ ubuntu-latest] 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Install required packages 19 | run: sudo apt-get install premake4 build-essential libglfw3-dev libglew-dev pkg-config 20 | 21 | - name: Premake 22 | run: premake4 gmake 23 | 24 | # due to glew problems with in the current ubuntu-latest, we don't build the examples (yet) 25 | # https://github.com/openai/mujoco-py/issues/383 has the same problem for reference 26 | # this doesn't happen in focal 27 | - name: Make 28 | run: cd build && make nanovg 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Compiled source # 2 | *.com 3 | *.class 4 | *.dll 5 | *.exe 6 | *.o 7 | *.so 8 | test 9 | 10 | ## Logs and databases # 11 | *.log 12 | *.sql 13 | *.sqlite 14 | 15 | ## OS generated files # 16 | .DS_Store 17 | .DS_Store? 18 | ._* 19 | .Spotlight-V100 20 | .Trashes 21 | ehthumbs.db 22 | Thumbs.db 23 | 24 | ## Build dir 25 | build/* 26 | 27 | ## xcode specific 28 | *xcuserdata* 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Mikko Mononen memon@inside.org 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *This project is not actively maintained.* 2 | 3 | NanoVG 4 | ========== 5 | 6 | NanoVG is small antialiased vector graphics rendering library for OpenGL. It has lean API modeled after HTML5 canvas API. It is aimed to be a practical and fun toolset for building scalable user interfaces and visualizations. 7 | 8 | ## Screenshot 9 | 10 | ![screenshot of some text rendered witht the sample program](/example/screenshot-01.png?raw=true) 11 | 12 | Usage 13 | ===== 14 | 15 | The NanoVG API is modeled loosely on HTML5 canvas API. If you know canvas, you're up to speed with NanoVG in no time. 16 | 17 | ## Creating drawing context 18 | 19 | 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: 20 | ```C 21 | #define NANOVG_GL2_IMPLEMENTATION // Use GL2 implementation. 22 | #include "nanovg_gl.h" 23 | ... 24 | struct NVGcontext* vg = nvgCreateGL2(NVG_ANTIALIAS | NVG_STENCIL_STROKES); 25 | ``` 26 | 27 | The first parameter defines flags for creating the renderer. 28 | 29 | - `NVG_ANTIALIAS` means that the renderer adjusts the geometry to include anti-aliasing. If you're using MSAA, you can omit this flags. 30 | - `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. 31 | 32 | 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. 33 | 34 | *NOTE:* The render target you're rendering to must have stencil buffer. 35 | 36 | ## Drawing shapes with NanoVG 37 | 38 | Drawing a simple shape using NanoVG consists of four steps: 1) begin a new shape, 2) define the path to draw, 3) set fill or stroke, 4) and finally fill or stroke the path. 39 | 40 | ```C 41 | nvgBeginPath(vg); 42 | nvgRect(vg, 100,100, 120,30); 43 | nvgFillColor(vg, nvgRGBA(255,192,0,255)); 44 | nvgFill(vg); 45 | ``` 46 | 47 | Calling `nvgBeginPath()` will clear any existing paths and start drawing from blank slate. There are number of number of functions to define the path to draw, such as rectangle, rounded rectangle and ellipse, or you can use the common moveTo, lineTo, bezierTo and arcTo API to compose the paths step by step. 48 | 49 | ## Understanding Composite Paths 50 | 51 | Because of the way the rendering backend is build in NanoVG, drawing a composite path, that is path consisting from multiple paths defining holes and fills, is a bit more involved. NanoVG uses even-odd filling rule and by default the paths are wound in counter clockwise order. Keep that in mind when drawing using the low level draw API. In order to wind one of the predefined shapes as a hole, you should call `nvgPathWinding(vg, NVG_HOLE)`, or `nvgPathWinding(vg, NVG_CW)` _after_ defining the path. 52 | 53 | ``` C 54 | nvgBeginPath(vg); 55 | nvgRect(vg, 100,100, 120,30); 56 | nvgCircle(vg, 120,120, 5); 57 | nvgPathWinding(vg, NVG_HOLE); // Mark circle as a hole. 58 | nvgFillColor(vg, nvgRGBA(255,192,0,255)); 59 | nvgFill(vg); 60 | ``` 61 | 62 | ## Rendering is wrong, what to do? 63 | 64 | - make sure you have created NanoVG context using one of the `nvgCreatexxx()` calls 65 | - make sure you have initialised OpenGL with *stencil buffer* 66 | - make sure you have cleared stencil buffer 67 | - make sure all rendering calls happen between `nvgBeginFrame()` and `nvgEndFrame()` 68 | - to enable more checks for OpenGL errors, add `NVG_DEBUG` flag to `nvgCreatexxx()` 69 | - if the problem still persists, please report an issue! 70 | 71 | ## OpenGL state touched by the backend 72 | 73 | The OpenGL back-end touches following states: 74 | 75 | When textures are uploaded or updated, the following pixel store is set to defaults: `GL_UNPACK_ALIGNMENT`, `GL_UNPACK_ROW_LENGTH`, `GL_UNPACK_SKIP_PIXELS`, `GL_UNPACK_SKIP_ROWS`. Texture binding is also affected. Texture updates can happen when the user loads images, or when new font glyphs are added. Glyphs are added as needed between calls to `nvgBeginFrame()` and `nvgEndFrame()`. 76 | 77 | The data for the whole frame is buffered and flushed in `nvgEndFrame()`. The following code illustrates the OpenGL state touched by the rendering code: 78 | ```C 79 | glUseProgram(prog); 80 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 81 | glEnable(GL_CULL_FACE); 82 | glCullFace(GL_BACK); 83 | glFrontFace(GL_CCW); 84 | glEnable(GL_BLEND); 85 | glDisable(GL_DEPTH_TEST); 86 | glDisable(GL_SCISSOR_TEST); 87 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 88 | glStencilMask(0xffffffff); 89 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 90 | glStencilFunc(GL_ALWAYS, 0, 0xffffffff); 91 | glActiveTexture(GL_TEXTURE0); 92 | glBindBuffer(GL_UNIFORM_BUFFER, buf); 93 | glBindVertexArray(arr); 94 | glBindBuffer(GL_ARRAY_BUFFER, buf); 95 | glBindTexture(GL_TEXTURE_2D, tex); 96 | glUniformBlockBinding(... , GLNVG_FRAG_BINDING); 97 | ``` 98 | 99 | ## API Reference 100 | 101 | See the header file [nanovg.h](/src/nanovg.h) for API reference. 102 | 103 | ## Ports 104 | 105 | - [DX11 port](https://github.com/cmaughan/nanovg) by [Chris Maughan](https://github.com/cmaughan) 106 | - [Metal port](https://github.com/ollix/MetalNanoVG) by [Olli Wang](https://github.com/olliwang) 107 | - [bgfx port](https://github.com/bkaradzic/bgfx/tree/master/examples/20-nanovg) by [Branimir Karadžić](https://github.com/bkaradzic) 108 | 109 | ## Projects using NanoVG 110 | 111 | - [Processing API simulation by vinjn](https://github.com/island-org/island/blob/master/include/sketch2d.h) 112 | - [NanoVG for .NET, C# P/Invoke binding](https://github.com/sbarisic/nanovg_dotnet) 113 | 114 | ## License 115 | The library is licensed under [zlib license](LICENSE.txt) 116 | Fonts used in examples: 117 | - Roboto licensed under [Apache license](http://www.apache.org/licenses/LICENSE-2.0) 118 | - Entypo licensed under CC BY-SA 4.0. 119 | - Noto Emoji licensed under [SIL Open Font License, Version 1.1](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) 120 | 121 | ## Discussions 122 | [NanoVG mailing list](https://groups.google.com/forum/#!forum/nanovg) 123 | 124 | ## Links 125 | Uses [stb_truetype](http://nothings.org) (or, optionally, [freetype](http://freetype.org)) for font rendering. 126 | Uses [stb_image](http://nothings.org) for image loading. 127 | -------------------------------------------------------------------------------- /example/LICENSE_OFL.txt: -------------------------------------------------------------------------------- 1 | This Font Software is licensed under the SIL Open Font License, 2 | Version 1.1. 3 | 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font 14 | creation efforts of academic and linguistic communities, and to 15 | provide a free and open framework in which fonts may be shared and 16 | improved in partnership with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply to 25 | any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software 36 | components as distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, 39 | deleting, or substituting -- in part or in whole -- any of the 40 | components of the Original Version, by changing formats or by porting 41 | the Font Software to a new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, 49 | modify, redistribute, and sell modified and unmodified copies of the 50 | Font Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, in 53 | Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the 64 | corresponding Copyright Holder. This restriction only applies to the 65 | primary font name as presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created using 77 | the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /example/NotoEmoji-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/NotoEmoji-Regular.ttf -------------------------------------------------------------------------------- /example/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/Roboto-Bold.ttf -------------------------------------------------------------------------------- /example/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/Roboto-Light.ttf -------------------------------------------------------------------------------- /example/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/Roboto-Regular.ttf -------------------------------------------------------------------------------- /example/demo.c: -------------------------------------------------------------------------------- 1 | #include "demo.h" 2 | #include 3 | #include 4 | #include 5 | #ifdef NANOVG_GLEW 6 | # include 7 | #endif 8 | #include 9 | #include "nanovg.h" 10 | #define STB_IMAGE_WRITE_IMPLEMENTATION 11 | #include "stb_image_write.h" 12 | 13 | 14 | #ifdef _MSC_VER 15 | #define snprintf _snprintf 16 | #elif !defined(__MINGW32__) 17 | #include 18 | #endif 19 | 20 | #define ICON_SEARCH 0x1F50D 21 | #define ICON_CIRCLED_CROSS 0x2716 22 | #define ICON_CHEVRON_RIGHT 0xE75E 23 | #define ICON_CHECK 0x2713 24 | #define ICON_LOGIN 0xE740 25 | #define ICON_TRASH 0xE729 26 | 27 | //static float minf(float a, float b) { return a < b ? a : b; } 28 | //static float maxf(float a, float b) { return a > b ? a : b; } 29 | //static float absf(float a) { return a >= 0.0f ? a : -a; } 30 | static float clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } 31 | 32 | // Returns 1 if col.rgba is 0.0f,0.0f,0.0f,0.0f, 0 otherwise 33 | int isBlack(NVGcolor col) 34 | { 35 | if( col.r == 0.0f && col.g == 0.0f && col.b == 0.0f && col.a == 0.0f ) 36 | { 37 | return 1; 38 | } 39 | return 0; 40 | } 41 | 42 | static char* cpToUTF8(int cp, char* str) 43 | { 44 | int n = 0; 45 | if (cp < 0x80) n = 1; 46 | else if (cp < 0x800) n = 2; 47 | else if (cp < 0x10000) n = 3; 48 | else if (cp < 0x200000) n = 4; 49 | else if (cp < 0x4000000) n = 5; 50 | else if (cp <= 0x7fffffff) n = 6; 51 | str[n] = '\0'; 52 | switch (n) { 53 | case 6: str[5] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x4000000; 54 | case 5: str[4] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x200000; 55 | case 4: str[3] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x10000; 56 | case 3: str[2] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x800; 57 | case 2: str[1] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0xc0; 58 | case 1: str[0] = cp; 59 | } 60 | return str; 61 | } 62 | 63 | 64 | void drawWindow(NVGcontext* vg, const char* title, float x, float y, float w, float h) 65 | { 66 | float cornerRadius = 3.0f; 67 | NVGpaint shadowPaint; 68 | NVGpaint headerPaint; 69 | 70 | nvgSave(vg); 71 | // nvgClearState(vg); 72 | 73 | // Window 74 | nvgBeginPath(vg); 75 | nvgRoundedRect(vg, x,y, w,h, cornerRadius); 76 | nvgFillColor(vg, nvgRGBA(28,30,34,192)); 77 | // nvgFillColor(vg, nvgRGBA(0,0,0,128)); 78 | nvgFill(vg); 79 | 80 | // Drop shadow 81 | shadowPaint = nvgBoxGradient(vg, x,y+2, w,h, cornerRadius*2, 10, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0)); 82 | nvgBeginPath(vg); 83 | nvgRect(vg, x-10,y-10, w+20,h+30); 84 | nvgRoundedRect(vg, x,y, w,h, cornerRadius); 85 | nvgPathWinding(vg, NVG_HOLE); 86 | nvgFillPaint(vg, shadowPaint); 87 | nvgFill(vg); 88 | 89 | // Header 90 | headerPaint = nvgLinearGradient(vg, x,y,x,y+15, nvgRGBA(255,255,255,8), nvgRGBA(0,0,0,16)); 91 | nvgBeginPath(vg); 92 | nvgRoundedRect(vg, x+1,y+1, w-2,30, cornerRadius-1); 93 | nvgFillPaint(vg, headerPaint); 94 | nvgFill(vg); 95 | nvgBeginPath(vg); 96 | nvgMoveTo(vg, x+0.5f, y+0.5f+30); 97 | nvgLineTo(vg, x+0.5f+w-1, y+0.5f+30); 98 | nvgStrokeColor(vg, nvgRGBA(0,0,0,32)); 99 | nvgStroke(vg); 100 | 101 | nvgFontSize(vg, 15.0f); 102 | nvgFontFace(vg, "sans-bold"); 103 | nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); 104 | 105 | nvgFontBlur(vg,2); 106 | nvgFillColor(vg, nvgRGBA(0,0,0,128)); 107 | nvgText(vg, x+w/2,y+16+1, title, NULL); 108 | 109 | nvgFontBlur(vg,0); 110 | nvgFillColor(vg, nvgRGBA(220,220,220,160)); 111 | nvgText(vg, x+w/2,y+16, title, NULL); 112 | 113 | nvgRestore(vg); 114 | } 115 | 116 | void drawSearchBox(NVGcontext* vg, const char* text, float x, float y, float w, float h) 117 | { 118 | NVGpaint bg; 119 | char icon[8]; 120 | float cornerRadius = h/2-1; 121 | 122 | // Edit 123 | bg = nvgBoxGradient(vg, x,y+1.5f, w,h, h/2,5, nvgRGBA(0,0,0,16), nvgRGBA(0,0,0,92)); 124 | nvgBeginPath(vg); 125 | nvgRoundedRect(vg, x,y, w,h, cornerRadius); 126 | nvgFillPaint(vg, bg); 127 | nvgFill(vg); 128 | 129 | /* nvgBeginPath(vg); 130 | nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f); 131 | nvgStrokeColor(vg, nvgRGBA(0,0,0,48)); 132 | nvgStroke(vg);*/ 133 | 134 | nvgFontSize(vg, h*1.3f); 135 | nvgFontFace(vg, "icons"); 136 | nvgFillColor(vg, nvgRGBA(255,255,255,64)); 137 | nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); 138 | nvgText(vg, x+h*0.55f, y+h*0.55f, cpToUTF8(ICON_SEARCH,icon), NULL); 139 | 140 | nvgFontSize(vg, 17.0f); 141 | nvgFontFace(vg, "sans"); 142 | nvgFillColor(vg, nvgRGBA(255,255,255,32)); 143 | 144 | nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); 145 | nvgText(vg, x+h*1.05f,y+h*0.5f,text, NULL); 146 | 147 | nvgFontSize(vg, h*1.3f); 148 | nvgFontFace(vg, "icons"); 149 | nvgFillColor(vg, nvgRGBA(255,255,255,32)); 150 | nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); 151 | nvgText(vg, x+w-h*0.55f, y+h*0.55f, cpToUTF8(ICON_CIRCLED_CROSS,icon), NULL); 152 | } 153 | 154 | void drawDropDown(NVGcontext* vg, const char* text, float x, float y, float w, float h) 155 | { 156 | NVGpaint bg; 157 | char icon[8]; 158 | float cornerRadius = 4.0f; 159 | 160 | bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16)); 161 | nvgBeginPath(vg); 162 | nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1); 163 | nvgFillPaint(vg, bg); 164 | nvgFill(vg); 165 | 166 | nvgBeginPath(vg); 167 | nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f); 168 | nvgStrokeColor(vg, nvgRGBA(0,0,0,48)); 169 | nvgStroke(vg); 170 | 171 | nvgFontSize(vg, 17.0f); 172 | nvgFontFace(vg, "sans"); 173 | nvgFillColor(vg, nvgRGBA(255,255,255,160)); 174 | nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); 175 | nvgText(vg, x+h*0.3f,y+h*0.5f,text, NULL); 176 | 177 | nvgFontSize(vg, h*1.3f); 178 | nvgFontFace(vg, "icons"); 179 | nvgFillColor(vg, nvgRGBA(255,255,255,64)); 180 | nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); 181 | nvgText(vg, x+w-h*0.5f, y+h*0.5f, cpToUTF8(ICON_CHEVRON_RIGHT,icon), NULL); 182 | } 183 | 184 | void drawLabel(NVGcontext* vg, const char* text, float x, float y, float w, float h) 185 | { 186 | NVG_NOTUSED(w); 187 | 188 | nvgFontSize(vg, 15.0f); 189 | nvgFontFace(vg, "sans"); 190 | nvgFillColor(vg, nvgRGBA(255,255,255,128)); 191 | 192 | nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); 193 | nvgText(vg, x,y+h*0.5f,text, NULL); 194 | } 195 | 196 | void drawEditBoxBase(NVGcontext* vg, float x, float y, float w, float h) 197 | { 198 | NVGpaint bg; 199 | // Edit 200 | 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)); 201 | nvgBeginPath(vg); 202 | nvgRoundedRect(vg, x+1,y+1, w-2,h-2, 4-1); 203 | nvgFillPaint(vg, bg); 204 | nvgFill(vg); 205 | 206 | nvgBeginPath(vg); 207 | nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, 4-0.5f); 208 | nvgStrokeColor(vg, nvgRGBA(0,0,0,48)); 209 | nvgStroke(vg); 210 | } 211 | 212 | void drawEditBox(NVGcontext* vg, const char* text, float x, float y, float w, float h) 213 | { 214 | 215 | drawEditBoxBase(vg, x,y, w,h); 216 | 217 | nvgFontSize(vg, 17.0f); 218 | nvgFontFace(vg, "sans"); 219 | nvgFillColor(vg, nvgRGBA(255,255,255,64)); 220 | nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); 221 | nvgText(vg, x+h*0.3f,y+h*0.5f,text, NULL); 222 | } 223 | 224 | void drawEditBoxNum(NVGcontext* vg, 225 | const char* text, const char* units, float x, float y, float w, float h) 226 | { 227 | float uw; 228 | 229 | drawEditBoxBase(vg, x,y, w,h); 230 | 231 | uw = nvgTextBounds(vg, 0,0, units, NULL, NULL); 232 | 233 | nvgFontSize(vg, 15.0f); 234 | nvgFontFace(vg, "sans"); 235 | nvgFillColor(vg, nvgRGBA(255,255,255,64)); 236 | nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); 237 | nvgText(vg, x+w-h*0.3f,y+h*0.5f,units, NULL); 238 | 239 | nvgFontSize(vg, 17.0f); 240 | nvgFontFace(vg, "sans"); 241 | nvgFillColor(vg, nvgRGBA(255,255,255,128)); 242 | nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); 243 | nvgText(vg, x+w-uw-h*0.5f,y+h*0.5f,text, NULL); 244 | } 245 | 246 | void drawCheckBox(NVGcontext* vg, const char* text, float x, float y, float w, float h) 247 | { 248 | NVGpaint bg; 249 | char icon[8]; 250 | NVG_NOTUSED(w); 251 | 252 | nvgFontSize(vg, 15.0f); 253 | nvgFontFace(vg, "sans"); 254 | nvgFillColor(vg, nvgRGBA(255,255,255,160)); 255 | 256 | nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); 257 | nvgText(vg, x+28,y+h*0.5f,text, NULL); 258 | 259 | 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)); 260 | nvgBeginPath(vg); 261 | nvgRoundedRect(vg, x+1,y+(int)(h*0.5f)-9, 18,18, 3); 262 | nvgFillPaint(vg, bg); 263 | nvgFill(vg); 264 | 265 | nvgFontSize(vg, 33); 266 | nvgFontFace(vg, "icons"); 267 | nvgFillColor(vg, nvgRGBA(255,255,255,128)); 268 | nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); 269 | nvgText(vg, x+9+2, y+h*0.5f, cpToUTF8(ICON_CHECK,icon), NULL); 270 | } 271 | 272 | void drawButton(NVGcontext* vg, int preicon, const char* text, float x, float y, float w, float h, NVGcolor col) 273 | { 274 | NVGpaint bg; 275 | char icon[8]; 276 | float cornerRadius = 4.0f; 277 | float tw = 0, iw = 0; 278 | 279 | bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,isBlack(col)?16:32), nvgRGBA(0,0,0,isBlack(col)?16:32)); 280 | nvgBeginPath(vg); 281 | nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1); 282 | if (!isBlack(col)) { 283 | nvgFillColor(vg, col); 284 | nvgFill(vg); 285 | } 286 | nvgFillPaint(vg, bg); 287 | nvgFill(vg); 288 | 289 | nvgBeginPath(vg); 290 | nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f); 291 | nvgStrokeColor(vg, nvgRGBA(0,0,0,48)); 292 | nvgStroke(vg); 293 | 294 | nvgFontSize(vg, 17.0f); 295 | nvgFontFace(vg, "sans-bold"); 296 | tw = nvgTextBounds(vg, 0,0, text, NULL, NULL); 297 | if (preicon != 0) { 298 | nvgFontSize(vg, h*1.3f); 299 | nvgFontFace(vg, "icons"); 300 | iw = nvgTextBounds(vg, 0,0, cpToUTF8(preicon,icon), NULL, NULL); 301 | iw += h*0.15f; 302 | } 303 | 304 | if (preicon != 0) { 305 | nvgFontSize(vg, h*1.3f); 306 | nvgFontFace(vg, "icons"); 307 | nvgFillColor(vg, nvgRGBA(255,255,255,96)); 308 | nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); 309 | nvgText(vg, x+w*0.5f-tw*0.5f-iw*0.75f, y+h*0.5f, cpToUTF8(preicon,icon), NULL); 310 | } 311 | 312 | nvgFontSize(vg, 17.0f); 313 | nvgFontFace(vg, "sans-bold"); 314 | nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); 315 | nvgFillColor(vg, nvgRGBA(0,0,0,160)); 316 | nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f-1,text, NULL); 317 | nvgFillColor(vg, nvgRGBA(255,255,255,160)); 318 | nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f,text, NULL); 319 | } 320 | 321 | void drawSlider(NVGcontext* vg, float pos, float x, float y, float w, float h) 322 | { 323 | NVGpaint bg, knob; 324 | float cy = y+(int)(h*0.5f); 325 | float kr = (int)(h*0.25f); 326 | 327 | nvgSave(vg); 328 | // nvgClearState(vg); 329 | 330 | // Slot 331 | bg = nvgBoxGradient(vg, x,cy-2+1, w,4, 2,2, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,128)); 332 | nvgBeginPath(vg); 333 | nvgRoundedRect(vg, x,cy-2, w,4, 2); 334 | nvgFillPaint(vg, bg); 335 | nvgFill(vg); 336 | 337 | // Knob Shadow 338 | bg = nvgRadialGradient(vg, x+(int)(pos*w),cy+1, kr-3,kr+3, nvgRGBA(0,0,0,64), nvgRGBA(0,0,0,0)); 339 | nvgBeginPath(vg); 340 | nvgRect(vg, x+(int)(pos*w)-kr-5,cy-kr-5,kr*2+5+5,kr*2+5+5+3); 341 | nvgCircle(vg, x+(int)(pos*w),cy, kr); 342 | nvgPathWinding(vg, NVG_HOLE); 343 | nvgFillPaint(vg, bg); 344 | nvgFill(vg); 345 | 346 | // Knob 347 | knob = nvgLinearGradient(vg, x,cy-kr,x,cy+kr, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16)); 348 | nvgBeginPath(vg); 349 | nvgCircle(vg, x+(int)(pos*w),cy, kr-1); 350 | nvgFillColor(vg, nvgRGBA(40,43,48,255)); 351 | nvgFill(vg); 352 | nvgFillPaint(vg, knob); 353 | nvgFill(vg); 354 | 355 | nvgBeginPath(vg); 356 | nvgCircle(vg, x+(int)(pos*w),cy, kr-0.5f); 357 | nvgStrokeColor(vg, nvgRGBA(0,0,0,92)); 358 | nvgStroke(vg); 359 | 360 | nvgRestore(vg); 361 | } 362 | 363 | void drawEyes(NVGcontext* vg, float x, float y, float w, float h, float mx, float my, float t) 364 | { 365 | NVGpaint gloss, bg; 366 | float ex = w *0.23f; 367 | float ey = h * 0.5f; 368 | float lx = x + ex; 369 | float ly = y + ey; 370 | float rx = x + w - ex; 371 | float ry = y + ey; 372 | float dx,dy,d; 373 | float br = (ex < ey ? ex : ey) * 0.5f; 374 | float blink = 1 - pow(sinf(t*0.5f),200)*0.8f; 375 | 376 | bg = nvgLinearGradient(vg, x,y+h*0.5f,x+w*0.1f,y+h, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,16)); 377 | nvgBeginPath(vg); 378 | nvgEllipse(vg, lx+3.0f,ly+16.0f, ex,ey); 379 | nvgEllipse(vg, rx+3.0f,ry+16.0f, ex,ey); 380 | nvgFillPaint(vg, bg); 381 | nvgFill(vg); 382 | 383 | bg = nvgLinearGradient(vg, x,y+h*0.25f,x+w*0.1f,y+h, nvgRGBA(220,220,220,255), nvgRGBA(128,128,128,255)); 384 | nvgBeginPath(vg); 385 | nvgEllipse(vg, lx,ly, ex,ey); 386 | nvgEllipse(vg, rx,ry, ex,ey); 387 | nvgFillPaint(vg, bg); 388 | nvgFill(vg); 389 | 390 | dx = (mx - rx) / (ex * 10); 391 | dy = (my - ry) / (ey * 10); 392 | d = sqrtf(dx*dx+dy*dy); 393 | if (d > 1.0f) { 394 | dx /= d; dy /= d; 395 | } 396 | dx *= ex*0.4f; 397 | dy *= ey*0.5f; 398 | nvgBeginPath(vg); 399 | nvgEllipse(vg, lx+dx,ly+dy+ey*0.25f*(1-blink), br,br*blink); 400 | nvgFillColor(vg, nvgRGBA(32,32,32,255)); 401 | nvgFill(vg); 402 | 403 | dx = (mx - rx) / (ex * 10); 404 | dy = (my - ry) / (ey * 10); 405 | d = sqrtf(dx*dx+dy*dy); 406 | if (d > 1.0f) { 407 | dx /= d; dy /= d; 408 | } 409 | dx *= ex*0.4f; 410 | dy *= ey*0.5f; 411 | nvgBeginPath(vg); 412 | nvgEllipse(vg, rx+dx,ry+dy+ey*0.25f*(1-blink), br,br*blink); 413 | nvgFillColor(vg, nvgRGBA(32,32,32,255)); 414 | nvgFill(vg); 415 | 416 | 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)); 417 | nvgBeginPath(vg); 418 | nvgEllipse(vg, lx,ly, ex,ey); 419 | nvgFillPaint(vg, gloss); 420 | nvgFill(vg); 421 | 422 | 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)); 423 | nvgBeginPath(vg); 424 | nvgEllipse(vg, rx,ry, ex,ey); 425 | nvgFillPaint(vg, gloss); 426 | nvgFill(vg); 427 | } 428 | 429 | void drawGraph(NVGcontext* vg, float x, float y, float w, float h, float t) 430 | { 431 | NVGpaint bg; 432 | float samples[6]; 433 | float sx[6], sy[6]; 434 | float dx = w/5.0f; 435 | int i; 436 | 437 | samples[0] = (1+sinf(t*1.2345f+cosf(t*0.33457f)*0.44f))*0.5f; 438 | samples[1] = (1+sinf(t*0.68363f+cosf(t*1.3f)*1.55f))*0.5f; 439 | samples[2] = (1+sinf(t*1.1642f+cosf(t*0.33457)*1.24f))*0.5f; 440 | samples[3] = (1+sinf(t*0.56345f+cosf(t*1.63f)*0.14f))*0.5f; 441 | samples[4] = (1+sinf(t*1.6245f+cosf(t*0.254f)*0.3f))*0.5f; 442 | samples[5] = (1+sinf(t*0.345f+cosf(t*0.03f)*0.6f))*0.5f; 443 | 444 | for (i = 0; i < 6; i++) { 445 | sx[i] = x+i*dx; 446 | sy[i] = y+h*samples[i]*0.8f; 447 | } 448 | 449 | // Graph background 450 | bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(0,160,192,0), nvgRGBA(0,160,192,64)); 451 | nvgBeginPath(vg); 452 | nvgMoveTo(vg, sx[0], sy[0]); 453 | for (i = 1; i < 6; i++) 454 | nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]); 455 | nvgLineTo(vg, x+w, y+h); 456 | nvgLineTo(vg, x, y+h); 457 | nvgFillPaint(vg, bg); 458 | nvgFill(vg); 459 | 460 | // Graph line 461 | nvgBeginPath(vg); 462 | nvgMoveTo(vg, sx[0], sy[0]+2); 463 | for (i = 1; i < 6; i++) 464 | 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); 465 | nvgStrokeColor(vg, nvgRGBA(0,0,0,32)); 466 | nvgStrokeWidth(vg, 3.0f); 467 | nvgStroke(vg); 468 | 469 | nvgBeginPath(vg); 470 | nvgMoveTo(vg, sx[0], sy[0]); 471 | for (i = 1; i < 6; i++) 472 | nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]); 473 | nvgStrokeColor(vg, nvgRGBA(0,160,192,255)); 474 | nvgStrokeWidth(vg, 3.0f); 475 | nvgStroke(vg); 476 | 477 | // Graph sample pos 478 | for (i = 0; i < 6; i++) { 479 | bg = nvgRadialGradient(vg, sx[i],sy[i]+2, 3.0f,8.0f, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,0)); 480 | nvgBeginPath(vg); 481 | nvgRect(vg, sx[i]-10, sy[i]-10+2, 20,20); 482 | nvgFillPaint(vg, bg); 483 | nvgFill(vg); 484 | } 485 | 486 | nvgBeginPath(vg); 487 | for (i = 0; i < 6; i++) 488 | nvgCircle(vg, sx[i], sy[i], 4.0f); 489 | nvgFillColor(vg, nvgRGBA(0,160,192,255)); 490 | nvgFill(vg); 491 | nvgBeginPath(vg); 492 | for (i = 0; i < 6; i++) 493 | nvgCircle(vg, sx[i], sy[i], 2.0f); 494 | nvgFillColor(vg, nvgRGBA(220,220,220,255)); 495 | nvgFill(vg); 496 | 497 | nvgStrokeWidth(vg, 1.0f); 498 | } 499 | 500 | void drawSpinner(NVGcontext* vg, float cx, float cy, float r, float t) 501 | { 502 | float a0 = 0.0f + t*6; 503 | float a1 = NVG_PI + t*6; 504 | float r0 = r; 505 | float r1 = r * 0.75f; 506 | float ax,ay, bx,by; 507 | NVGpaint paint; 508 | 509 | nvgSave(vg); 510 | 511 | nvgBeginPath(vg); 512 | nvgArc(vg, cx,cy, r0, a0, a1, NVG_CW); 513 | nvgArc(vg, cx,cy, r1, a1, a0, NVG_CCW); 514 | nvgClosePath(vg); 515 | ax = cx + cosf(a0) * (r0+r1)*0.5f; 516 | ay = cy + sinf(a0) * (r0+r1)*0.5f; 517 | bx = cx + cosf(a1) * (r0+r1)*0.5f; 518 | by = cy + sinf(a1) * (r0+r1)*0.5f; 519 | paint = nvgLinearGradient(vg, ax,ay, bx,by, nvgRGBA(0,0,0,0), nvgRGBA(0,0,0,128)); 520 | nvgFillPaint(vg, paint); 521 | nvgFill(vg); 522 | 523 | nvgRestore(vg); 524 | } 525 | 526 | void drawThumbnails(NVGcontext* vg, float x, float y, float w, float h, const int* images, int nimages, float t) 527 | { 528 | float cornerRadius = 3.0f; 529 | NVGpaint shadowPaint, imgPaint, fadePaint; 530 | float ix,iy,iw,ih; 531 | float thumb = 60.0f; 532 | float arry = 30.5f; 533 | int imgw, imgh; 534 | float stackh = (nimages/2) * (thumb+10) + 10; 535 | int i; 536 | float u = (1+cosf(t*0.5f))*0.5f; 537 | float u2 = (1-cosf(t*0.2f))*0.5f; 538 | float scrollh, dv; 539 | 540 | nvgSave(vg); 541 | // nvgClearState(vg); 542 | 543 | // Drop shadow 544 | shadowPaint = nvgBoxGradient(vg, x,y+4, w,h, cornerRadius*2, 20, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0)); 545 | nvgBeginPath(vg); 546 | nvgRect(vg, x-10,y-10, w+20,h+30); 547 | nvgRoundedRect(vg, x,y, w,h, cornerRadius); 548 | nvgPathWinding(vg, NVG_HOLE); 549 | nvgFillPaint(vg, shadowPaint); 550 | nvgFill(vg); 551 | 552 | // Window 553 | nvgBeginPath(vg); 554 | nvgRoundedRect(vg, x,y, w,h, cornerRadius); 555 | nvgMoveTo(vg, x-10,y+arry); 556 | nvgLineTo(vg, x+1,y+arry-11); 557 | nvgLineTo(vg, x+1,y+arry+11); 558 | nvgFillColor(vg, nvgRGBA(200,200,200,255)); 559 | nvgFill(vg); 560 | 561 | nvgSave(vg); 562 | nvgScissor(vg, x,y,w,h); 563 | nvgTranslate(vg, 0, -(stackh - h)*u); 564 | 565 | dv = 1.0f / (float)(nimages-1); 566 | 567 | for (i = 0; i < nimages; i++) { 568 | float tx, ty, v, a; 569 | tx = x+10; 570 | ty = y+10; 571 | tx += (i%2) * (thumb+10); 572 | ty += (i/2) * (thumb+10); 573 | nvgImageSize(vg, images[i], &imgw, &imgh); 574 | if (imgw < imgh) { 575 | iw = thumb; 576 | ih = iw * (float)imgh/(float)imgw; 577 | ix = 0; 578 | iy = -(ih-thumb)*0.5f; 579 | } else { 580 | ih = thumb; 581 | iw = ih * (float)imgw/(float)imgh; 582 | ix = -(iw-thumb)*0.5f; 583 | iy = 0; 584 | } 585 | 586 | v = i * dv; 587 | a = clampf((u2-v) / dv, 0, 1); 588 | 589 | if (a < 1.0f) 590 | drawSpinner(vg, tx+thumb/2,ty+thumb/2, thumb*0.25f, t); 591 | 592 | imgPaint = nvgImagePattern(vg, tx+ix, ty+iy, iw,ih, 0.0f/180.0f*NVG_PI, images[i], a); 593 | nvgBeginPath(vg); 594 | nvgRoundedRect(vg, tx,ty, thumb,thumb, 5); 595 | nvgFillPaint(vg, imgPaint); 596 | nvgFill(vg); 597 | 598 | shadowPaint = nvgBoxGradient(vg, tx-1,ty, thumb+2,thumb+2, 5, 3, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0)); 599 | nvgBeginPath(vg); 600 | nvgRect(vg, tx-5,ty-5, thumb+10,thumb+10); 601 | nvgRoundedRect(vg, tx,ty, thumb,thumb, 6); 602 | nvgPathWinding(vg, NVG_HOLE); 603 | nvgFillPaint(vg, shadowPaint); 604 | nvgFill(vg); 605 | 606 | nvgBeginPath(vg); 607 | nvgRoundedRect(vg, tx+0.5f,ty+0.5f, thumb-1,thumb-1, 4-0.5f); 608 | nvgStrokeWidth(vg,1.0f); 609 | nvgStrokeColor(vg, nvgRGBA(255,255,255,192)); 610 | nvgStroke(vg); 611 | } 612 | nvgRestore(vg); 613 | 614 | // Hide fades 615 | fadePaint = nvgLinearGradient(vg, x,y,x,y+6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0)); 616 | nvgBeginPath(vg); 617 | nvgRect(vg, x+4,y,w-8,6); 618 | nvgFillPaint(vg, fadePaint); 619 | nvgFill(vg); 620 | 621 | fadePaint = nvgLinearGradient(vg, x,y+h,x,y+h-6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0)); 622 | nvgBeginPath(vg); 623 | nvgRect(vg, x+4,y+h-6,w-8,6); 624 | nvgFillPaint(vg, fadePaint); 625 | nvgFill(vg); 626 | 627 | // Scroll bar 628 | 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)); 629 | nvgBeginPath(vg); 630 | nvgRoundedRect(vg, x+w-12,y+4, 8,h-8, 3); 631 | nvgFillPaint(vg, shadowPaint); 632 | // nvgFillColor(vg, nvgRGBA(255,0,0,128)); 633 | nvgFill(vg); 634 | 635 | scrollh = (h/stackh) * (h-8); 636 | 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)); 637 | nvgBeginPath(vg); 638 | nvgRoundedRect(vg, x+w-12+1,y+4+1 + (h-8-scrollh)*u, 8-2,scrollh-2, 2); 639 | nvgFillPaint(vg, shadowPaint); 640 | // nvgFillColor(vg, nvgRGBA(0,0,0,128)); 641 | nvgFill(vg); 642 | 643 | nvgRestore(vg); 644 | } 645 | 646 | void drawColorwheel(NVGcontext* vg, float x, float y, float w, float h, float t) 647 | { 648 | int i; 649 | float r0, r1, ax,ay, bx,by, cx,cy, aeps, r; 650 | float hue = sinf(t * 0.12f); 651 | NVGpaint paint; 652 | 653 | nvgSave(vg); 654 | 655 | /* nvgBeginPath(vg); 656 | nvgRect(vg, x,y,w,h); 657 | nvgFillColor(vg, nvgRGBA(255,0,0,128)); 658 | nvgFill(vg);*/ 659 | 660 | cx = x + w*0.5f; 661 | cy = y + h*0.5f; 662 | r1 = (w < h ? w : h) * 0.5f - 5.0f; 663 | r0 = r1 - 20.0f; 664 | aeps = 0.5f / r1; // half a pixel arc length in radians (2pi cancels out). 665 | 666 | for (i = 0; i < 6; i++) { 667 | float a0 = (float)i / 6.0f * NVG_PI * 2.0f - aeps; 668 | float a1 = (float)(i+1.0f) / 6.0f * NVG_PI * 2.0f + aeps; 669 | nvgBeginPath(vg); 670 | nvgArc(vg, cx,cy, r0, a0, a1, NVG_CW); 671 | nvgArc(vg, cx,cy, r1, a1, a0, NVG_CCW); 672 | nvgClosePath(vg); 673 | ax = cx + cosf(a0) * (r0+r1)*0.5f; 674 | ay = cy + sinf(a0) * (r0+r1)*0.5f; 675 | bx = cx + cosf(a1) * (r0+r1)*0.5f; 676 | by = cy + sinf(a1) * (r0+r1)*0.5f; 677 | 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)); 678 | nvgFillPaint(vg, paint); 679 | nvgFill(vg); 680 | } 681 | 682 | nvgBeginPath(vg); 683 | nvgCircle(vg, cx,cy, r0-0.5f); 684 | nvgCircle(vg, cx,cy, r1+0.5f); 685 | nvgStrokeColor(vg, nvgRGBA(0,0,0,64)); 686 | nvgStrokeWidth(vg, 1.0f); 687 | nvgStroke(vg); 688 | 689 | // Selector 690 | nvgSave(vg); 691 | nvgTranslate(vg, cx,cy); 692 | nvgRotate(vg, hue*NVG_PI*2); 693 | 694 | // Marker on 695 | nvgStrokeWidth(vg, 2.0f); 696 | nvgBeginPath(vg); 697 | nvgRect(vg, r0-1,-3,r1-r0+2,6); 698 | nvgStrokeColor(vg, nvgRGBA(255,255,255,192)); 699 | nvgStroke(vg); 700 | 701 | paint = nvgBoxGradient(vg, r0-3,-5,r1-r0+6,10, 2,4, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0)); 702 | nvgBeginPath(vg); 703 | nvgRect(vg, r0-2-10,-4-10,r1-r0+4+20,8+20); 704 | nvgRect(vg, r0-2,-4,r1-r0+4,8); 705 | nvgPathWinding(vg, NVG_HOLE); 706 | nvgFillPaint(vg, paint); 707 | nvgFill(vg); 708 | 709 | // Center triangle 710 | r = r0 - 6; 711 | ax = cosf(120.0f/180.0f*NVG_PI) * r; 712 | ay = sinf(120.0f/180.0f*NVG_PI) * r; 713 | bx = cosf(-120.0f/180.0f*NVG_PI) * r; 714 | by = sinf(-120.0f/180.0f*NVG_PI) * r; 715 | nvgBeginPath(vg); 716 | nvgMoveTo(vg, r,0); 717 | nvgLineTo(vg, ax,ay); 718 | nvgLineTo(vg, bx,by); 719 | nvgClosePath(vg); 720 | paint = nvgLinearGradient(vg, r,0, ax,ay, nvgHSLA(hue,1.0f,0.5f,255), nvgRGBA(255,255,255,255)); 721 | nvgFillPaint(vg, paint); 722 | nvgFill(vg); 723 | paint = nvgLinearGradient(vg, (r+ax)*0.5f,(0+ay)*0.5f, bx,by, nvgRGBA(0,0,0,0), nvgRGBA(0,0,0,255)); 724 | nvgFillPaint(vg, paint); 725 | nvgFill(vg); 726 | nvgStrokeColor(vg, nvgRGBA(0,0,0,64)); 727 | nvgStroke(vg); 728 | 729 | // Select circle on triangle 730 | ax = cosf(120.0f/180.0f*NVG_PI) * r*0.3f; 731 | ay = sinf(120.0f/180.0f*NVG_PI) * r*0.4f; 732 | nvgStrokeWidth(vg, 2.0f); 733 | nvgBeginPath(vg); 734 | nvgCircle(vg, ax,ay,5); 735 | nvgStrokeColor(vg, nvgRGBA(255,255,255,192)); 736 | nvgStroke(vg); 737 | 738 | paint = nvgRadialGradient(vg, ax,ay, 7,9, nvgRGBA(0,0,0,64), nvgRGBA(0,0,0,0)); 739 | nvgBeginPath(vg); 740 | nvgRect(vg, ax-20,ay-20,40,40); 741 | nvgCircle(vg, ax,ay,7); 742 | nvgPathWinding(vg, NVG_HOLE); 743 | nvgFillPaint(vg, paint); 744 | nvgFill(vg); 745 | 746 | nvgRestore(vg); 747 | 748 | nvgRestore(vg); 749 | } 750 | 751 | void drawLines(NVGcontext* vg, float x, float y, float w, float h, float t) 752 | { 753 | int i, j; 754 | float pad = 5.0f, s = w/9.0f - pad*2; 755 | float pts[4*2], fx, fy; 756 | int joins[3] = {NVG_MITER, NVG_ROUND, NVG_BEVEL}; 757 | int caps[3] = {NVG_BUTT, NVG_ROUND, NVG_SQUARE}; 758 | NVG_NOTUSED(h); 759 | 760 | nvgSave(vg); 761 | pts[0] = -s*0.25f + cosf(t*0.3f) * s*0.5f; 762 | pts[1] = sinf(t*0.3f) * s*0.5f; 763 | pts[2] = -s*0.25; 764 | pts[3] = 0; 765 | pts[4] = s*0.25f; 766 | pts[5] = 0; 767 | pts[6] = s*0.25f + cosf(-t*0.3f) * s*0.5f; 768 | pts[7] = sinf(-t*0.3f) * s*0.5f; 769 | 770 | for (i = 0; i < 3; i++) { 771 | for (j = 0; j < 3; j++) { 772 | fx = x + s*0.5f + (i*3+j)/9.0f*w + pad; 773 | fy = y - s*0.5f + pad; 774 | 775 | nvgLineCap(vg, caps[i]); 776 | nvgLineJoin(vg, joins[j]); 777 | 778 | nvgStrokeWidth(vg, s*0.3f); 779 | nvgStrokeColor(vg, nvgRGBA(0,0,0,160)); 780 | nvgBeginPath(vg); 781 | nvgMoveTo(vg, fx+pts[0], fy+pts[1]); 782 | nvgLineTo(vg, fx+pts[2], fy+pts[3]); 783 | nvgLineTo(vg, fx+pts[4], fy+pts[5]); 784 | nvgLineTo(vg, fx+pts[6], fy+pts[7]); 785 | nvgStroke(vg); 786 | 787 | nvgLineCap(vg, NVG_BUTT); 788 | nvgLineJoin(vg, NVG_BEVEL); 789 | 790 | nvgStrokeWidth(vg, 1.0f); 791 | nvgStrokeColor(vg, nvgRGBA(0,192,255,255)); 792 | nvgBeginPath(vg); 793 | nvgMoveTo(vg, fx+pts[0], fy+pts[1]); 794 | nvgLineTo(vg, fx+pts[2], fy+pts[3]); 795 | nvgLineTo(vg, fx+pts[4], fy+pts[5]); 796 | nvgLineTo(vg, fx+pts[6], fy+pts[7]); 797 | nvgStroke(vg); 798 | } 799 | } 800 | 801 | 802 | nvgRestore(vg); 803 | } 804 | 805 | int loadDemoData(NVGcontext* vg, DemoData* data) 806 | { 807 | int i; 808 | 809 | if (vg == NULL) 810 | return -1; 811 | 812 | for (i = 0; i < 12; i++) { 813 | char file[128]; 814 | snprintf(file, 128, "../example/images/image%d.jpg", i+1); 815 | data->images[i] = nvgCreateImage(vg, file, 0); 816 | if (data->images[i] == 0) { 817 | printf("Could not load %s.\n", file); 818 | return -1; 819 | } 820 | } 821 | 822 | data->fontIcons = nvgCreateFont(vg, "icons", "../example/entypo.ttf"); 823 | if (data->fontIcons == -1) { 824 | printf("Could not add font icons.\n"); 825 | return -1; 826 | } 827 | data->fontNormal = nvgCreateFont(vg, "sans", "../example/Roboto-Regular.ttf"); 828 | if (data->fontNormal == -1) { 829 | printf("Could not add font italic.\n"); 830 | return -1; 831 | } 832 | data->fontBold = nvgCreateFont(vg, "sans-bold", "../example/Roboto-Bold.ttf"); 833 | if (data->fontBold == -1) { 834 | printf("Could not add font bold.\n"); 835 | return -1; 836 | } 837 | data->fontEmoji = nvgCreateFont(vg, "emoji", "../example/NotoEmoji-Regular.ttf"); 838 | if (data->fontEmoji == -1) { 839 | printf("Could not add font emoji.\n"); 840 | return -1; 841 | } 842 | nvgAddFallbackFontId(vg, data->fontNormal, data->fontEmoji); 843 | nvgAddFallbackFontId(vg, data->fontBold, data->fontEmoji); 844 | 845 | return 0; 846 | } 847 | 848 | void freeDemoData(NVGcontext* vg, DemoData* data) 849 | { 850 | int i; 851 | 852 | if (vg == NULL) 853 | return; 854 | 855 | for (i = 0; i < 12; i++) 856 | nvgDeleteImage(vg, data->images[i]); 857 | } 858 | 859 | void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, float mx, float my) 860 | { 861 | NVGtextRow rows[3]; 862 | NVGglyphPosition glyphs[100]; 863 | 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.🎉"; 864 | const char* start; 865 | const char* end; 866 | int nrows, i, nglyphs, j, lnum = 0; 867 | float lineh; 868 | float caretx, px; 869 | float bounds[4]; 870 | float a; 871 | const char* hoverText = "Hover your mouse over the text to see calculated caret position."; 872 | float gx,gy; 873 | int gutter = 0; 874 | const char* boxText = "Testing\nsome multiline\ntext."; 875 | NVG_NOTUSED(height); 876 | 877 | nvgSave(vg); 878 | 879 | nvgFontSize(vg, 15.0f); 880 | nvgFontFace(vg, "sans"); 881 | nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); 882 | nvgTextMetrics(vg, NULL, NULL, &lineh); 883 | 884 | // The text break API can be used to fill a large buffer of rows, 885 | // or to iterate over the text just few lines (or just one) at a time. 886 | // The "next" variable of the last returned item tells where to continue. 887 | start = text; 888 | end = text + strlen(text); 889 | while ((nrows = nvgTextBreakLines(vg, start, end, width, rows, 3))) { 890 | for (i = 0; i < nrows; i++) { 891 | NVGtextRow* row = &rows[i]; 892 | int hit = mx > x && mx < (x+width) && my >= y && my < (y+lineh); 893 | 894 | nvgBeginPath(vg); 895 | nvgFillColor(vg, nvgRGBA(255,255,255,hit?64:16)); 896 | nvgRect(vg, x + row->minx, y, row->maxx - row->minx, lineh); 897 | nvgFill(vg); 898 | 899 | nvgFillColor(vg, nvgRGBA(255,255,255,255)); 900 | nvgText(vg, x, y, row->start, row->end); 901 | 902 | if (hit) { 903 | caretx = (mx < x+row->width/2) ? x : x+row->width; 904 | px = x; 905 | nglyphs = nvgTextGlyphPositions(vg, x, y, row->start, row->end, glyphs, 100); 906 | for (j = 0; j < nglyphs; j++) { 907 | float x0 = glyphs[j].x; 908 | float x1 = (j+1 < nglyphs) ? glyphs[j+1].x : x+row->width; 909 | float gx = x0 * 0.3f + x1 * 0.7f; 910 | if (mx >= px && mx < gx) 911 | caretx = glyphs[j].x; 912 | px = gx; 913 | } 914 | nvgBeginPath(vg); 915 | nvgFillColor(vg, nvgRGBA(255,192,0,255)); 916 | nvgRect(vg, caretx, y, 1, lineh); 917 | nvgFill(vg); 918 | 919 | gutter = lnum+1; 920 | gx = x - 10; 921 | gy = y + lineh/2; 922 | } 923 | lnum++; 924 | y += lineh; 925 | } 926 | // Keep going... 927 | start = rows[nrows-1].next; 928 | } 929 | 930 | if (gutter) { 931 | char txt[16]; 932 | snprintf(txt, sizeof(txt), "%d", gutter); 933 | nvgFontSize(vg, 12.0f); 934 | nvgTextAlign(vg, NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); 935 | 936 | nvgTextBounds(vg, gx,gy, txt, NULL, bounds); 937 | 938 | nvgBeginPath(vg); 939 | nvgFillColor(vg, nvgRGBA(255,192,0,255)); 940 | 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); 941 | nvgFill(vg); 942 | 943 | nvgFillColor(vg, nvgRGBA(32,32,32,255)); 944 | nvgText(vg, gx,gy, txt, NULL); 945 | } 946 | 947 | y += 20.0f; 948 | 949 | nvgFontSize(vg, 11.0f); 950 | nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); 951 | nvgTextLineHeight(vg, 1.2f); 952 | 953 | nvgTextBoxBounds(vg, x,y, 150, hoverText, NULL, bounds); 954 | 955 | // Fade the tooltip out when close to it. 956 | gx = clampf(mx, bounds[0], bounds[2]) - mx; 957 | gy = clampf(my, bounds[1], bounds[3]) - my; 958 | a = sqrtf(gx*gx + gy*gy) / 30.0f; 959 | a = clampf(a, 0, 1); 960 | nvgGlobalAlpha(vg, a); 961 | 962 | nvgBeginPath(vg); 963 | nvgFillColor(vg, nvgRGBA(220,220,220,255)); 964 | nvgRoundedRect(vg, bounds[0]-2,bounds[1]-2, (int)(bounds[2]-bounds[0])+4, (int)(bounds[3]-bounds[1])+4, 3); 965 | px = (int)((bounds[2]+bounds[0])/2); 966 | nvgMoveTo(vg, px,bounds[1] - 10); 967 | nvgLineTo(vg, px+7,bounds[1]+1); 968 | nvgLineTo(vg, px-7,bounds[1]+1); 969 | nvgFill(vg); 970 | 971 | nvgFillColor(vg, nvgRGBA(0,0,0,220)); 972 | nvgTextBox(vg, x,y, 150, hoverText, NULL); 973 | 974 | nvgRestore(vg); 975 | } 976 | 977 | void drawWidths(NVGcontext* vg, float x, float y, float width) 978 | { 979 | int i; 980 | 981 | nvgSave(vg); 982 | 983 | nvgStrokeColor(vg, nvgRGBA(0,0,0,255)); 984 | 985 | for (i = 0; i < 20; i++) { 986 | float w = (i+0.5f)*0.1f; 987 | nvgStrokeWidth(vg, w); 988 | nvgBeginPath(vg); 989 | nvgMoveTo(vg, x,y); 990 | nvgLineTo(vg, x+width,y+width*0.3f); 991 | nvgStroke(vg); 992 | y += 10; 993 | } 994 | 995 | nvgRestore(vg); 996 | } 997 | 998 | void drawCaps(NVGcontext* vg, float x, float y, float width) 999 | { 1000 | int i; 1001 | int caps[3] = {NVG_BUTT, NVG_ROUND, NVG_SQUARE}; 1002 | float lineWidth = 8.0f; 1003 | 1004 | nvgSave(vg); 1005 | 1006 | nvgBeginPath(vg); 1007 | nvgRect(vg, x-lineWidth/2, y, width+lineWidth, 40); 1008 | nvgFillColor(vg, nvgRGBA(255,255,255,32)); 1009 | nvgFill(vg); 1010 | 1011 | nvgBeginPath(vg); 1012 | nvgRect(vg, x, y, width, 40); 1013 | nvgFillColor(vg, nvgRGBA(255,255,255,32)); 1014 | nvgFill(vg); 1015 | 1016 | nvgStrokeWidth(vg, lineWidth); 1017 | for (i = 0; i < 3; i++) { 1018 | nvgLineCap(vg, caps[i]); 1019 | nvgStrokeColor(vg, nvgRGBA(0,0,0,255)); 1020 | nvgBeginPath(vg); 1021 | nvgMoveTo(vg, x, y + i*10 + 5); 1022 | nvgLineTo(vg, x+width, y + i*10 + 5); 1023 | nvgStroke(vg); 1024 | } 1025 | 1026 | nvgRestore(vg); 1027 | } 1028 | 1029 | void drawScissor(NVGcontext* vg, float x, float y, float t) 1030 | { 1031 | nvgSave(vg); 1032 | 1033 | // Draw first rect and set scissor to it's area. 1034 | nvgTranslate(vg, x, y); 1035 | nvgRotate(vg, nvgDegToRad(5)); 1036 | nvgBeginPath(vg); 1037 | nvgRect(vg, -20,-20,60,40); 1038 | nvgFillColor(vg, nvgRGBA(255,0,0,255)); 1039 | nvgFill(vg); 1040 | nvgScissor(vg, -20,-20,60,40); 1041 | 1042 | // Draw second rectangle with offset and rotation. 1043 | nvgTranslate(vg, 40,0); 1044 | nvgRotate(vg, t); 1045 | 1046 | // Draw the intended second rectangle without any scissoring. 1047 | nvgSave(vg); 1048 | nvgResetScissor(vg); 1049 | nvgBeginPath(vg); 1050 | nvgRect(vg, -20,-10,60,30); 1051 | nvgFillColor(vg, nvgRGBA(255,128,0,64)); 1052 | nvgFill(vg); 1053 | nvgRestore(vg); 1054 | 1055 | // Draw second rectangle with combined scissoring. 1056 | nvgIntersectScissor(vg, -20,-10,60,30); 1057 | nvgBeginPath(vg); 1058 | nvgRect(vg, -20,-10,60,30); 1059 | nvgFillColor(vg, nvgRGBA(255,128,0,255)); 1060 | nvgFill(vg); 1061 | 1062 | nvgRestore(vg); 1063 | } 1064 | 1065 | void renderDemo(NVGcontext* vg, float mx, float my, float width, float height, 1066 | float t, int blowup, DemoData* data) 1067 | { 1068 | float x,y,popy; 1069 | 1070 | drawEyes(vg, width - 250, 50, 150, 100, mx, my, t); 1071 | drawParagraph(vg, width - 450, 50, 150, 100, mx, my); 1072 | drawGraph(vg, 0, height/2, width, height/2, t); 1073 | drawColorwheel(vg, width - 300, height - 300, 250.0f, 250.0f, t); 1074 | 1075 | // Line joints 1076 | drawLines(vg, 120, height-50, 600, 50, t); 1077 | 1078 | // Line caps 1079 | drawWidths(vg, 10, 50, 30); 1080 | 1081 | // Line caps 1082 | drawCaps(vg, 10, 300, 30); 1083 | 1084 | drawScissor(vg, 50, height-80, t); 1085 | 1086 | nvgSave(vg); 1087 | if (blowup) { 1088 | nvgRotate(vg, sinf(t*0.3f)*5.0f/180.0f*NVG_PI); 1089 | nvgScale(vg, 2.0f, 2.0f); 1090 | } 1091 | 1092 | // Widgets 1093 | drawWindow(vg, "Widgets `n Stuff", 50, 50, 300, 400); 1094 | x = 60; y = 95; 1095 | drawSearchBox(vg, "Search", x,y,280,25); 1096 | y += 40; 1097 | drawDropDown(vg, "Effects", x,y,280,28); 1098 | popy = y + 14; 1099 | y += 45; 1100 | 1101 | // Form 1102 | drawLabel(vg, "Login", x,y, 280,20); 1103 | y += 25; 1104 | drawEditBox(vg, "Email", x,y, 280,28); 1105 | y += 35; 1106 | drawEditBox(vg, "Password", x,y, 280,28); 1107 | y += 38; 1108 | drawCheckBox(vg, "Remember me", x,y, 140,28); 1109 | drawButton(vg, ICON_LOGIN, "Sign in", x+138, y, 140, 28, nvgRGBA(0,96,128,255)); 1110 | y += 45; 1111 | 1112 | // Slider 1113 | drawLabel(vg, "Diameter", x,y, 280,20); 1114 | y += 25; 1115 | drawEditBoxNum(vg, "123.00", "px", x+180,y, 100,28); 1116 | drawSlider(vg, 0.4f, x,y, 170,28); 1117 | y += 55; 1118 | 1119 | drawButton(vg, ICON_TRASH, "Delete", x, y, 160, 28, nvgRGBA(128,16,8,255)); 1120 | drawButton(vg, 0, "Cancel", x+170, y, 110, 28, nvgRGBA(0,0,0,0)); 1121 | 1122 | // Thumbnails box 1123 | drawThumbnails(vg, 365, popy-30, 160, 300, data->images, 12, t); 1124 | 1125 | nvgRestore(vg); 1126 | } 1127 | 1128 | static int mini(int a, int b) { return a < b ? a : b; } 1129 | 1130 | static void unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) 1131 | { 1132 | int x,y; 1133 | 1134 | // Unpremultiply 1135 | for (y = 0; y < h; y++) { 1136 | unsigned char *row = &image[y*stride]; 1137 | for (x = 0; x < w; x++) { 1138 | int r = row[0], g = row[1], b = row[2], a = row[3]; 1139 | if (a != 0) { 1140 | row[0] = (int)mini(r*255/a, 255); 1141 | row[1] = (int)mini(g*255/a, 255); 1142 | row[2] = (int)mini(b*255/a, 255); 1143 | } 1144 | row += 4; 1145 | } 1146 | } 1147 | 1148 | // Defringe 1149 | for (y = 0; y < h; y++) { 1150 | unsigned char *row = &image[y*stride]; 1151 | for (x = 0; x < w; x++) { 1152 | int r = 0, g = 0, b = 0, a = row[3], n = 0; 1153 | if (a == 0) { 1154 | if (x-1 > 0 && row[-1] != 0) { 1155 | r += row[-4]; 1156 | g += row[-3]; 1157 | b += row[-2]; 1158 | n++; 1159 | } 1160 | if (x+1 < w && row[7] != 0) { 1161 | r += row[4]; 1162 | g += row[5]; 1163 | b += row[6]; 1164 | n++; 1165 | } 1166 | if (y-1 > 0 && row[-stride+3] != 0) { 1167 | r += row[-stride]; 1168 | g += row[-stride+1]; 1169 | b += row[-stride+2]; 1170 | n++; 1171 | } 1172 | if (y+1 < h && row[stride+3] != 0) { 1173 | r += row[stride]; 1174 | g += row[stride+1]; 1175 | b += row[stride+2]; 1176 | n++; 1177 | } 1178 | if (n > 0) { 1179 | row[0] = r/n; 1180 | row[1] = g/n; 1181 | row[2] = b/n; 1182 | } 1183 | } 1184 | row += 4; 1185 | } 1186 | } 1187 | } 1188 | 1189 | static void setAlpha(unsigned char* image, int w, int h, int stride, unsigned char a) 1190 | { 1191 | int x, y; 1192 | for (y = 0; y < h; y++) { 1193 | unsigned char* row = &image[y*stride]; 1194 | for (x = 0; x < w; x++) 1195 | row[x*4+3] = a; 1196 | } 1197 | } 1198 | 1199 | static void flipHorizontal(unsigned char* image, int w, int h, int stride) 1200 | { 1201 | int i = 0, j = h-1, k; 1202 | while (i < j) { 1203 | unsigned char* ri = &image[i * stride]; 1204 | unsigned char* rj = &image[j * stride]; 1205 | for (k = 0; k < w*4; k++) { 1206 | unsigned char t = ri[k]; 1207 | ri[k] = rj[k]; 1208 | rj[k] = t; 1209 | } 1210 | i++; 1211 | j--; 1212 | } 1213 | } 1214 | 1215 | void saveScreenShot(int w, int h, int premult, const char* name) 1216 | { 1217 | unsigned char* image = (unsigned char*)malloc(w*h*4); 1218 | if (image == NULL) 1219 | return; 1220 | glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image); 1221 | if (premult) 1222 | unpremultiplyAlpha(image, w, h, w*4); 1223 | else 1224 | setAlpha(image, w, h, w*4, 255); 1225 | flipHorizontal(image, w, h, w*4); 1226 | stbi_write_png(name, w, h, 4, image, w*4); 1227 | free(image); 1228 | } 1229 | -------------------------------------------------------------------------------- /example/demo.h: -------------------------------------------------------------------------------- 1 | #ifndef DEMO_H 2 | #define DEMO_H 3 | 4 | #include "nanovg.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | struct DemoData { 11 | int fontNormal, fontBold, fontIcons, fontEmoji; 12 | int images[12]; 13 | }; 14 | typedef struct DemoData DemoData; 15 | 16 | int loadDemoData(NVGcontext* vg, DemoData* data); 17 | void freeDemoData(NVGcontext* vg, DemoData* data); 18 | void renderDemo(NVGcontext* vg, float mx, float my, float width, float height, float t, int blowup, DemoData* data); 19 | 20 | void saveScreenShot(int w, int h, int premult, const char* name); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif // DEMO_H 27 | -------------------------------------------------------------------------------- /example/entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/entypo.ttf -------------------------------------------------------------------------------- /example/example_fbo.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #include 20 | #ifdef NANOVG_GLEW 21 | # include 22 | #endif 23 | #ifdef __APPLE__ 24 | # define GLFW_INCLUDE_GLCOREARB 25 | #endif 26 | #include 27 | #include "nanovg.h" 28 | #define NANOVG_GL3_IMPLEMENTATION 29 | #include "nanovg_gl.h" 30 | #include "nanovg_gl_utils.h" 31 | #include "perf.h" 32 | 33 | void renderPattern(NVGcontext* vg, NVGLUframebuffer* fb, float t, float pxRatio) 34 | { 35 | int winWidth, winHeight; 36 | int fboWidth, fboHeight; 37 | int pw, ph, x, y; 38 | float s = 20.0f; 39 | float sr = (cosf(t)+1)*0.5f; 40 | float r = s * 0.6f * (0.2f + 0.8f * sr); 41 | 42 | if (fb == NULL) return; 43 | 44 | nvgImageSize(vg, fb->image, &fboWidth, &fboHeight); 45 | winWidth = (int)(fboWidth / pxRatio); 46 | winHeight = (int)(fboHeight / pxRatio); 47 | 48 | // Draw some stuff to an FBO as a test 49 | nvgluBindFramebuffer(fb); 50 | glViewport(0, 0, fboWidth, fboHeight); 51 | glClearColor(0, 0, 0, 0); 52 | glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 53 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio); 54 | 55 | pw = (int)ceilf(winWidth / s); 56 | ph = (int)ceilf(winHeight / s); 57 | 58 | nvgBeginPath(vg); 59 | for (y = 0; y < ph; y++) { 60 | for (x = 0; x < pw; x++) { 61 | float cx = (x+0.5f) * s; 62 | float cy = (y+0.5f) * s; 63 | nvgCircle(vg, cx,cy, r); 64 | } 65 | } 66 | nvgFillColor(vg, nvgRGBA(220,160,0,200)); 67 | nvgFill(vg); 68 | 69 | nvgEndFrame(vg); 70 | nvgluBindFramebuffer(NULL); 71 | } 72 | 73 | int loadFonts(NVGcontext* vg) 74 | { 75 | int font; 76 | font = nvgCreateFont(vg, "sans", "../example/Roboto-Regular.ttf"); 77 | if (font == -1) { 78 | printf("Could not add font regular.\n"); 79 | return -1; 80 | } 81 | font = nvgCreateFont(vg, "sans-bold", "../example/Roboto-Bold.ttf"); 82 | if (font == -1) { 83 | printf("Could not add font bold.\n"); 84 | return -1; 85 | } 86 | return 0; 87 | } 88 | 89 | void errorcb(int error, const char* desc) 90 | { 91 | printf("GLFW error %d: %s\n", error, desc); 92 | } 93 | 94 | static void key(GLFWwindow* window, int key, int scancode, int action, int mods) 95 | { 96 | NVG_NOTUSED(scancode); 97 | NVG_NOTUSED(mods); 98 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 99 | glfwSetWindowShouldClose(window, GL_TRUE); 100 | } 101 | 102 | int main() 103 | { 104 | GLFWwindow* window; 105 | NVGcontext* vg = NULL; 106 | GPUtimer gpuTimer; 107 | PerfGraph fps, cpuGraph, gpuGraph; 108 | double prevt = 0, cpuTime = 0; 109 | NVGLUframebuffer* fb = NULL; 110 | int winWidth, winHeight; 111 | int fbWidth, fbHeight; 112 | float pxRatio; 113 | 114 | if (!glfwInit()) { 115 | printf("Failed to init GLFW."); 116 | return -1; 117 | } 118 | 119 | initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time"); 120 | initGraph(&cpuGraph, GRAPH_RENDER_MS, "CPU Time"); 121 | initGraph(&gpuGraph, GRAPH_RENDER_MS, "GPU Time"); 122 | 123 | glfwSetErrorCallback(errorcb); 124 | #ifndef _WIN32 // don't require this on win32, and works with more cards 125 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 126 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 127 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 128 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 129 | #endif 130 | glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1); 131 | 132 | #ifdef DEMO_MSAA 133 | glfwWindowHint(GLFW_SAMPLES, 4); 134 | #endif 135 | window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL); 136 | // window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL); 137 | if (!window) { 138 | glfwTerminate(); 139 | return -1; 140 | } 141 | 142 | glfwSetKeyCallback(window, key); 143 | 144 | glfwMakeContextCurrent(window); 145 | #ifdef NANOVG_GLEW 146 | glewExperimental = GL_TRUE; 147 | if(glewInit() != GLEW_OK) { 148 | printf("Could not init glew.\n"); 149 | return -1; 150 | } 151 | // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here. 152 | glGetError(); 153 | #endif 154 | 155 | #ifdef DEMO_MSAA 156 | vg = nvgCreateGL3(NVG_STENCIL_STROKES | NVG_DEBUG); 157 | #else 158 | vg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG); 159 | #endif 160 | if (vg == NULL) { 161 | printf("Could not init nanovg.\n"); 162 | return -1; 163 | } 164 | 165 | // Create hi-dpi FBO for hi-dpi screens. 166 | glfwGetWindowSize(window, &winWidth, &winHeight); 167 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 168 | // Calculate pixel ration for hi-dpi devices. 169 | pxRatio = (float)fbWidth / (float)winWidth; 170 | 171 | // The image pattern is tiled, set repeat on x and y. 172 | fb = nvgluCreateFramebuffer(vg, (int)(100*pxRatio), (int)(100*pxRatio), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); 173 | if (fb == NULL) { 174 | printf("Could not create FBO.\n"); 175 | return -1; 176 | } 177 | 178 | if (loadFonts(vg) == -1) { 179 | printf("Could not load fonts\n"); 180 | return -1; 181 | } 182 | 183 | glfwSwapInterval(0); 184 | 185 | initGPUTimer(&gpuTimer); 186 | 187 | glfwSetTime(0); 188 | prevt = glfwGetTime(); 189 | 190 | while (!glfwWindowShouldClose(window)) 191 | { 192 | double mx, my, t, dt; 193 | float gpuTimes[3]; 194 | int i, n; 195 | 196 | t = glfwGetTime(); 197 | dt = t - prevt; 198 | prevt = t; 199 | 200 | startGPUTimer(&gpuTimer); 201 | 202 | glfwGetCursorPos(window, &mx, &my); 203 | glfwGetWindowSize(window, &winWidth, &winHeight); 204 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 205 | // Calculate pixel ration for hi-dpi devices. 206 | pxRatio = (float)fbWidth / (float)winWidth; 207 | 208 | renderPattern(vg, fb, t, pxRatio); 209 | 210 | // Update and render 211 | glViewport(0, 0, fbWidth, fbHeight); 212 | glClearColor(0.3f, 0.3f, 0.32f, 1.0f); 213 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 214 | 215 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio); 216 | 217 | // Use the FBO as image pattern. 218 | if (fb != NULL) { 219 | NVGpaint img = nvgImagePattern(vg, 0, 0, 100, 100, 0, fb->image, 1.0f); 220 | nvgSave(vg); 221 | 222 | for (i = 0; i < 20; i++) { 223 | nvgBeginPath(vg); 224 | nvgRect(vg, 10 + i*30,10, 10, winHeight-20); 225 | nvgFillColor(vg, nvgHSLA(i/19.0f, 0.5f, 0.5f, 255)); 226 | nvgFill(vg); 227 | } 228 | 229 | nvgBeginPath(vg); 230 | nvgRoundedRect(vg, 140 + sinf(t*1.3f)*100, 140 + cosf(t*1.71244f)*100, 250, 250, 20); 231 | nvgFillPaint(vg, img); 232 | nvgFill(vg); 233 | nvgStrokeColor(vg, nvgRGBA(220,160,0,255)); 234 | nvgStrokeWidth(vg, 3.0f); 235 | nvgStroke(vg); 236 | 237 | nvgRestore(vg); 238 | } 239 | 240 | renderGraph(vg, 5,5, &fps); 241 | renderGraph(vg, 5+200+5,5, &cpuGraph); 242 | if (gpuTimer.supported) 243 | renderGraph(vg, 5+200+5+200+5,5, &gpuGraph); 244 | 245 | nvgEndFrame(vg); 246 | 247 | // Measure the CPU time taken excluding swap buffers (as the swap may wait for GPU) 248 | cpuTime = glfwGetTime() - t; 249 | 250 | updateGraph(&fps, dt); 251 | updateGraph(&cpuGraph, cpuTime); 252 | 253 | // We may get multiple results. 254 | n = stopGPUTimer(&gpuTimer, gpuTimes, 3); 255 | for (i = 0; i < n; i++) 256 | updateGraph(&gpuGraph, gpuTimes[i]); 257 | 258 | glfwSwapBuffers(window); 259 | glfwPollEvents(); 260 | } 261 | 262 | nvgluDeleteFramebuffer(fb); 263 | 264 | nvgDeleteGL3(vg); 265 | 266 | printf("Average Frame Time: %.2f ms\n", getGraphAverage(&fps) * 1000.0f); 267 | printf(" CPU Time: %.2f ms\n", getGraphAverage(&cpuGraph) * 1000.0f); 268 | printf(" GPU Time: %.2f ms\n", getGraphAverage(&gpuGraph) * 1000.0f); 269 | 270 | glfwTerminate(); 271 | return 0; 272 | } 273 | -------------------------------------------------------------------------------- /example/example_gl2.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #include 20 | #ifdef NANOVG_GLEW 21 | # include 22 | #endif 23 | #define GLFW_INCLUDE_GLEXT 24 | #include 25 | #include "nanovg.h" 26 | #define NANOVG_GL2_IMPLEMENTATION 27 | #include "nanovg_gl.h" 28 | #include "demo.h" 29 | #include "perf.h" 30 | 31 | 32 | void errorcb(int error, const char* desc) 33 | { 34 | printf("GLFW error %d: %s\n", error, desc); 35 | } 36 | 37 | int blowup = 0; 38 | int screenshot = 0; 39 | int premult = 0; 40 | 41 | static void key(GLFWwindow* window, int key, int scancode, int action, int mods) 42 | { 43 | NVG_NOTUSED(scancode); 44 | NVG_NOTUSED(mods); 45 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 46 | glfwSetWindowShouldClose(window, GL_TRUE); 47 | if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 48 | blowup = !blowup; 49 | if (key == GLFW_KEY_S && action == GLFW_PRESS) 50 | screenshot = 1; 51 | if (key == GLFW_KEY_P && action == GLFW_PRESS) 52 | premult = !premult; 53 | } 54 | 55 | int main() 56 | { 57 | GLFWwindow* window; 58 | DemoData data; 59 | NVGcontext* vg = NULL; 60 | PerfGraph fps; 61 | double prevt = 0; 62 | 63 | if (!glfwInit()) { 64 | printf("Failed to init GLFW."); 65 | return -1; 66 | } 67 | 68 | initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time"); 69 | 70 | glfwSetErrorCallback(errorcb); 71 | 72 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); 73 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 74 | #ifdef DEMO_MSAA 75 | glfwWindowHint(GLFW_SAMPLES, 4); 76 | #endif 77 | 78 | window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL); 79 | // window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL); 80 | if (!window) { 81 | glfwTerminate(); 82 | return -1; 83 | } 84 | 85 | glfwSetKeyCallback(window, key); 86 | 87 | glfwMakeContextCurrent(window); 88 | #ifdef NANOVG_GLEW 89 | if(glewInit() != GLEW_OK) { 90 | printf("Could not init glew.\n"); 91 | return -1; 92 | } 93 | #endif 94 | 95 | #ifdef DEMO_MSAA 96 | vg = nvgCreateGL2(NVG_STENCIL_STROKES | NVG_DEBUG); 97 | #else 98 | vg = nvgCreateGL2(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG); 99 | #endif 100 | if (vg == NULL) { 101 | printf("Could not init nanovg.\n"); 102 | return -1; 103 | } 104 | 105 | if (loadDemoData(vg, &data) == -1) 106 | return -1; 107 | 108 | glfwSwapInterval(0); 109 | 110 | glfwSetTime(0); 111 | prevt = glfwGetTime(); 112 | 113 | while (!glfwWindowShouldClose(window)) 114 | { 115 | double mx, my, t, dt; 116 | int winWidth, winHeight; 117 | int fbWidth, fbHeight; 118 | float pxRatio; 119 | 120 | t = glfwGetTime(); 121 | dt = t - prevt; 122 | prevt = t; 123 | updateGraph(&fps, dt); 124 | 125 | glfwGetCursorPos(window, &mx, &my); 126 | glfwGetWindowSize(window, &winWidth, &winHeight); 127 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 128 | 129 | // Calculate pixel ration for hi-dpi devices. 130 | pxRatio = (float)fbWidth / (float)winWidth; 131 | 132 | // Update and render 133 | glViewport(0, 0, fbWidth, fbHeight); 134 | if (premult) 135 | glClearColor(0,0,0,0); 136 | else 137 | glClearColor(0.3f, 0.3f, 0.32f, 1.0f); 138 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 139 | 140 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio); 141 | 142 | renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); 143 | renderGraph(vg, 5,5, &fps); 144 | 145 | nvgEndFrame(vg); 146 | 147 | if (screenshot) { 148 | screenshot = 0; 149 | saveScreenShot(fbWidth, fbHeight, premult, "dump.png"); 150 | } 151 | 152 | glfwSwapBuffers(window); 153 | glfwPollEvents(); 154 | } 155 | 156 | freeDemoData(vg, &data); 157 | 158 | nvgDeleteGL2(vg); 159 | 160 | glfwTerminate(); 161 | return 0; 162 | } 163 | -------------------------------------------------------------------------------- /example/example_gl3.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #include 20 | #ifdef NANOVG_GLEW 21 | # include 22 | #endif 23 | #ifdef __APPLE__ 24 | # define GLFW_INCLUDE_GLCOREARB 25 | #endif 26 | #define GLFW_INCLUDE_GLEXT 27 | #include 28 | #include "nanovg.h" 29 | #define NANOVG_GL3_IMPLEMENTATION 30 | #include "nanovg_gl.h" 31 | #include "demo.h" 32 | #include "perf.h" 33 | 34 | 35 | void errorcb(int error, const char* desc) 36 | { 37 | printf("GLFW error %d: %s\n", error, desc); 38 | } 39 | 40 | int blowup = 0; 41 | int screenshot = 0; 42 | int premult = 0; 43 | 44 | static void key(GLFWwindow* window, int key, int scancode, int action, int mods) 45 | { 46 | NVG_NOTUSED(scancode); 47 | NVG_NOTUSED(mods); 48 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 49 | glfwSetWindowShouldClose(window, GL_TRUE); 50 | if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 51 | blowup = !blowup; 52 | if (key == GLFW_KEY_S && action == GLFW_PRESS) 53 | screenshot = 1; 54 | if (key == GLFW_KEY_P && action == GLFW_PRESS) 55 | premult = !premult; 56 | } 57 | 58 | int main() 59 | { 60 | GLFWwindow* window; 61 | DemoData data; 62 | NVGcontext* vg = NULL; 63 | GPUtimer gpuTimer; 64 | PerfGraph fps, cpuGraph, gpuGraph; 65 | double prevt = 0, cpuTime = 0; 66 | 67 | if (!glfwInit()) { 68 | printf("Failed to init GLFW."); 69 | return -1; 70 | } 71 | 72 | initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time"); 73 | initGraph(&cpuGraph, GRAPH_RENDER_MS, "CPU Time"); 74 | initGraph(&gpuGraph, GRAPH_RENDER_MS, "GPU Time"); 75 | 76 | glfwSetErrorCallback(errorcb); 77 | #ifndef _WIN32 // don't require this on win32, and works with more cards 78 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 79 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 80 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 81 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 82 | #endif 83 | glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1); 84 | 85 | #ifdef DEMO_MSAA 86 | glfwWindowHint(GLFW_SAMPLES, 4); 87 | #endif 88 | window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL); 89 | // window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL); 90 | if (!window) { 91 | glfwTerminate(); 92 | return -1; 93 | } 94 | 95 | glfwSetKeyCallback(window, key); 96 | 97 | glfwMakeContextCurrent(window); 98 | #ifdef NANOVG_GLEW 99 | glewExperimental = GL_TRUE; 100 | if(glewInit() != GLEW_OK) { 101 | printf("Could not init glew.\n"); 102 | return -1; 103 | } 104 | // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here. 105 | glGetError(); 106 | #endif 107 | 108 | #ifdef DEMO_MSAA 109 | vg = nvgCreateGL3(NVG_STENCIL_STROKES | NVG_DEBUG); 110 | #else 111 | vg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG); 112 | #endif 113 | if (vg == NULL) { 114 | printf("Could not init nanovg.\n"); 115 | return -1; 116 | } 117 | 118 | if (loadDemoData(vg, &data) == -1) 119 | return -1; 120 | 121 | glfwSwapInterval(0); 122 | 123 | initGPUTimer(&gpuTimer); 124 | 125 | glfwSetTime(0); 126 | prevt = glfwGetTime(); 127 | 128 | while (!glfwWindowShouldClose(window)) 129 | { 130 | double mx, my, t, dt; 131 | int winWidth, winHeight; 132 | int fbWidth, fbHeight; 133 | float pxRatio; 134 | float gpuTimes[3]; 135 | int i, n; 136 | 137 | t = glfwGetTime(); 138 | dt = t - prevt; 139 | prevt = t; 140 | 141 | startGPUTimer(&gpuTimer); 142 | 143 | glfwGetCursorPos(window, &mx, &my); 144 | glfwGetWindowSize(window, &winWidth, &winHeight); 145 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 146 | // Calculate pixel ration for hi-dpi devices. 147 | pxRatio = (float)fbWidth / (float)winWidth; 148 | 149 | // Update and render 150 | glViewport(0, 0, fbWidth, fbHeight); 151 | if (premult) 152 | glClearColor(0,0,0,0); 153 | else 154 | glClearColor(0.3f, 0.3f, 0.32f, 1.0f); 155 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 156 | 157 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio); 158 | 159 | renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); 160 | 161 | renderGraph(vg, 5,5, &fps); 162 | renderGraph(vg, 5+200+5,5, &cpuGraph); 163 | if (gpuTimer.supported) 164 | renderGraph(vg, 5+200+5+200+5,5, &gpuGraph); 165 | 166 | nvgEndFrame(vg); 167 | 168 | // Measure the CPU time taken excluding swap buffers (as the swap may wait for GPU) 169 | cpuTime = glfwGetTime() - t; 170 | 171 | updateGraph(&fps, dt); 172 | updateGraph(&cpuGraph, cpuTime); 173 | 174 | // We may get multiple results. 175 | n = stopGPUTimer(&gpuTimer, gpuTimes, 3); 176 | for (i = 0; i < n; i++) 177 | updateGraph(&gpuGraph, gpuTimes[i]); 178 | 179 | if (screenshot) { 180 | screenshot = 0; 181 | saveScreenShot(fbWidth, fbHeight, premult, "dump.png"); 182 | } 183 | 184 | glfwSwapBuffers(window); 185 | glfwPollEvents(); 186 | } 187 | 188 | freeDemoData(vg, &data); 189 | 190 | nvgDeleteGL3(vg); 191 | 192 | printf("Average Frame Time: %.2f ms\n", getGraphAverage(&fps) * 1000.0f); 193 | printf(" CPU Time: %.2f ms\n", getGraphAverage(&cpuGraph) * 1000.0f); 194 | printf(" GPU Time: %.2f ms\n", getGraphAverage(&gpuGraph) * 1000.0f); 195 | 196 | glfwTerminate(); 197 | return 0; 198 | } 199 | -------------------------------------------------------------------------------- /example/example_gles2.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #include 20 | #define GLFW_INCLUDE_ES2 21 | #define GLFW_INCLUDE_GLEXT 22 | #include 23 | #include "nanovg.h" 24 | #define NANOVG_GLES2_IMPLEMENTATION 25 | #include "nanovg_gl.h" 26 | #include "nanovg_gl_utils.h" 27 | #include "demo.h" 28 | #include "perf.h" 29 | 30 | 31 | void errorcb(int error, const char* desc) 32 | { 33 | printf("GLFW error %d: %s\n", error, desc); 34 | } 35 | 36 | int blowup = 0; 37 | int screenshot = 0; 38 | int premult = 0; 39 | 40 | static void key(GLFWwindow* window, int key, int scancode, int action, int mods) 41 | { 42 | NVG_NOTUSED(scancode); 43 | NVG_NOTUSED(mods); 44 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 45 | glfwSetWindowShouldClose(window, GL_TRUE); 46 | if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 47 | blowup = !blowup; 48 | if (key == GLFW_KEY_S && action == GLFW_PRESS) 49 | screenshot = 1; 50 | if (key == GLFW_KEY_P && action == GLFW_PRESS) 51 | premult = !premult; 52 | } 53 | 54 | int main() 55 | { 56 | GLFWwindow* window; 57 | DemoData data; 58 | NVGcontext* vg = NULL; 59 | PerfGraph fps; 60 | double prevt = 0; 61 | 62 | if (!glfwInit()) { 63 | printf("Failed to init GLFW."); 64 | return -1; 65 | } 66 | 67 | initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time"); 68 | 69 | glfwSetErrorCallback(errorcb); 70 | 71 | glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); 72 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); 73 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 74 | 75 | window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL); 76 | // window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL); 77 | if (!window) { 78 | glfwTerminate(); 79 | return -1; 80 | } 81 | 82 | glfwSetKeyCallback(window, key); 83 | 84 | glfwMakeContextCurrent(window); 85 | 86 | vg = nvgCreateGLES2(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG); 87 | if (vg == NULL) { 88 | printf("Could not init nanovg.\n"); 89 | return -1; 90 | } 91 | 92 | if (loadDemoData(vg, &data) == -1) 93 | return -1; 94 | 95 | glfwSwapInterval(0); 96 | 97 | glfwSetTime(0); 98 | prevt = glfwGetTime(); 99 | 100 | while (!glfwWindowShouldClose(window)) 101 | { 102 | double mx, my, t, dt; 103 | int winWidth, winHeight; 104 | int fbWidth, fbHeight; 105 | float pxRatio; 106 | 107 | t = glfwGetTime(); 108 | dt = t - prevt; 109 | prevt = t; 110 | updateGraph(&fps, dt); 111 | 112 | glfwGetCursorPos(window, &mx, &my); 113 | glfwGetWindowSize(window, &winWidth, &winHeight); 114 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 115 | // Calculate pixel ration for hi-dpi devices. 116 | pxRatio = (float)fbWidth / (float)winWidth; 117 | 118 | // Update and render 119 | glViewport(0, 0, fbWidth, fbHeight); 120 | if (premult) 121 | glClearColor(0,0,0,0); 122 | else 123 | glClearColor(0.3f, 0.3f, 0.32f, 1.0f); 124 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 125 | 126 | glEnable(GL_BLEND); 127 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 128 | glEnable(GL_CULL_FACE); 129 | glDisable(GL_DEPTH_TEST); 130 | 131 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio); 132 | 133 | renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); 134 | renderGraph(vg, 5,5, &fps); 135 | 136 | nvgEndFrame(vg); 137 | 138 | if (screenshot) { 139 | screenshot = 0; 140 | saveScreenShot(fbWidth, fbHeight, premult, "dump.png"); 141 | } 142 | 143 | glEnable(GL_DEPTH_TEST); 144 | 145 | glfwSwapBuffers(window); 146 | glfwPollEvents(); 147 | } 148 | 149 | freeDemoData(vg, &data); 150 | 151 | nvgDeleteGLES2(vg); 152 | 153 | glfwTerminate(); 154 | return 0; 155 | } 156 | -------------------------------------------------------------------------------- /example/example_gles3.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #include 20 | #define GLFW_INCLUDE_ES3 21 | #define GLFW_INCLUDE_GLEXT 22 | #include 23 | #include "nanovg.h" 24 | #define NANOVG_GLES3_IMPLEMENTATION 25 | #include "nanovg_gl.h" 26 | #include "nanovg_gl_utils.h" 27 | #include "demo.h" 28 | #include "perf.h" 29 | 30 | 31 | void errorcb(int error, const char* desc) 32 | { 33 | printf("GLFW error %d: %s\n", error, desc); 34 | } 35 | 36 | int blowup = 0; 37 | int screenshot = 0; 38 | int premult = 0; 39 | 40 | static void key(GLFWwindow* window, int key, int scancode, int action, int mods) 41 | { 42 | NVG_NOTUSED(scancode); 43 | NVG_NOTUSED(mods); 44 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 45 | glfwSetWindowShouldClose(window, GL_TRUE); 46 | if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 47 | blowup = !blowup; 48 | if (key == GLFW_KEY_S && action == GLFW_PRESS) 49 | screenshot = 1; 50 | if (key == GLFW_KEY_P && action == GLFW_PRESS) 51 | premult = !premult; 52 | } 53 | 54 | int main() 55 | { 56 | GLFWwindow* window; 57 | DemoData data; 58 | NVGcontext* vg = NULL; 59 | PerfGraph fps; 60 | double prevt = 0; 61 | 62 | if (!glfwInit()) { 63 | printf("Failed to init GLFW."); 64 | return -1; 65 | } 66 | 67 | initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time"); 68 | 69 | glfwSetErrorCallback(errorcb); 70 | 71 | glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); 72 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 73 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 74 | 75 | window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL); 76 | // window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL); 77 | if (!window) { 78 | glfwTerminate(); 79 | return -1; 80 | } 81 | 82 | glfwSetKeyCallback(window, key); 83 | 84 | glfwMakeContextCurrent(window); 85 | 86 | vg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG); 87 | if (vg == NULL) { 88 | printf("Could not init nanovg.\n"); 89 | return -1; 90 | } 91 | 92 | if (loadDemoData(vg, &data) == -1) 93 | return -1; 94 | 95 | glfwSwapInterval(0); 96 | 97 | glfwSetTime(0); 98 | prevt = glfwGetTime(); 99 | 100 | while (!glfwWindowShouldClose(window)) 101 | { 102 | double mx, my, t, dt; 103 | int winWidth, winHeight; 104 | int fbWidth, fbHeight; 105 | float pxRatio; 106 | 107 | t = glfwGetTime(); 108 | dt = t - prevt; 109 | prevt = t; 110 | updateGraph(&fps, dt); 111 | 112 | glfwGetCursorPos(window, &mx, &my); 113 | glfwGetWindowSize(window, &winWidth, &winHeight); 114 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 115 | // Calculate pixel ration for hi-dpi devices. 116 | pxRatio = (float)fbWidth / (float)winWidth; 117 | 118 | // Update and render 119 | glViewport(0, 0, fbWidth, fbHeight); 120 | if (premult) 121 | glClearColor(0,0,0,0); 122 | else 123 | glClearColor(0.3f, 0.3f, 0.32f, 1.0f); 124 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 125 | 126 | glEnable(GL_BLEND); 127 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 128 | glEnable(GL_CULL_FACE); 129 | glDisable(GL_DEPTH_TEST); 130 | 131 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio); 132 | 133 | renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); 134 | renderGraph(vg, 5,5, &fps); 135 | 136 | nvgEndFrame(vg); 137 | 138 | glEnable(GL_DEPTH_TEST); 139 | 140 | if (screenshot) { 141 | screenshot = 0; 142 | saveScreenShot(fbWidth, fbHeight, premult, "dump.png"); 143 | } 144 | 145 | glfwSwapBuffers(window); 146 | glfwPollEvents(); 147 | } 148 | 149 | freeDemoData(vg, &data); 150 | 151 | nvgDeleteGLES3(vg); 152 | 153 | glfwTerminate(); 154 | return 0; 155 | } 156 | -------------------------------------------------------------------------------- /example/images.txt: -------------------------------------------------------------------------------- 1 | Image credits 2 | http://cuteoverload.com/2013/11/05/mom-taxi-xvi-birthday-party/ 3 | http://cuteoverload.com/2013/11/05/benson-hedges-private-eye-in-the-case-of-the-crafty-craftsman/ 4 | http://cuteoverload.com/2013/11/05/no-underwater-ballets/ 5 | http://cuteoverload.com/2013/11/05/every-nose-has-a-story/ 6 | http://cuteoverload.com/2013/11/04/nosevember-nozzle-nose/ 7 | http://cuteoverload.com/2013/11/04/this-just-in-super-strength-cute/ 8 | http://cuteoverload.com/2013/11/03/have-a-bunderful-sunday/ 9 | http://cuteoverload.com/2013/11/02/caturday-sense-a-common-theme-here/ 10 | http://cuteoverload.com/2013/11/01/nosevember-1st-24-hours-of-noses-1148pm-pt/ 11 | http://cuteoverload.com/2013/04/02/there-might-be-something-cuter-than-this/ 12 | http://cuteoverload.com/2013/07/17/snorting-micro-peeg-gets-belleh-rubs-interwebs-explode/ 13 | http://cuteoverload.com/2013/08/07/bark-in-the-park-v3-0/ -------------------------------------------------------------------------------- /example/images/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image1.jpg -------------------------------------------------------------------------------- /example/images/image10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image10.jpg -------------------------------------------------------------------------------- /example/images/image11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image11.jpg -------------------------------------------------------------------------------- /example/images/image12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image12.jpg -------------------------------------------------------------------------------- /example/images/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image2.jpg -------------------------------------------------------------------------------- /example/images/image3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image3.jpg -------------------------------------------------------------------------------- /example/images/image4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image4.jpg -------------------------------------------------------------------------------- /example/images/image5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image5.jpg -------------------------------------------------------------------------------- /example/images/image6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image6.jpg -------------------------------------------------------------------------------- /example/images/image7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image7.jpg -------------------------------------------------------------------------------- /example/images/image8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image8.jpg -------------------------------------------------------------------------------- /example/images/image9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/images/image9.jpg -------------------------------------------------------------------------------- /example/perf.c: -------------------------------------------------------------------------------- 1 | #include "perf.h" 2 | #include 3 | #include 4 | #include 5 | #ifdef NANOVG_GLEW 6 | # include 7 | #endif 8 | #include 9 | #include "nanovg.h" 10 | 11 | #ifdef _MSC_VER 12 | #define snprintf _snprintf 13 | #elif !defined(__MINGW32__) 14 | #include 15 | #endif 16 | 17 | // timer query support 18 | #ifndef GL_ARB_timer_query 19 | #define GL_TIME_ELAPSED 0x88BF 20 | //typedef void (APIENTRY *pfnGLGETQUERYOBJECTUI64V)(GLuint id, GLenum pname, GLuint64* params); 21 | //pfnGLGETQUERYOBJECTUI64V glGetQueryObjectui64v = 0; 22 | #endif 23 | 24 | void initGPUTimer(GPUtimer* timer) 25 | { 26 | memset(timer, 0, sizeof(*timer)); 27 | 28 | /* timer->supported = glfwExtensionSupported("GL_ARB_timer_query"); 29 | if (timer->supported) { 30 | #ifndef GL_ARB_timer_query 31 | glGetQueryObjectui64v = (pfnGLGETQUERYOBJECTUI64V)glfwGetProcAddress("glGetQueryObjectui64v"); 32 | printf("glGetQueryObjectui64v=%p\n", glGetQueryObjectui64v); 33 | if (!glGetQueryObjectui64v) { 34 | timer->supported = GL_FALSE; 35 | return; 36 | } 37 | #endif 38 | glGenQueries(GPU_QUERY_COUNT, timer->queries); 39 | }*/ 40 | } 41 | 42 | void startGPUTimer(GPUtimer* timer) 43 | { 44 | if (!timer->supported) 45 | return; 46 | glBeginQuery(GL_TIME_ELAPSED, timer->queries[timer->cur % GPU_QUERY_COUNT] ); 47 | timer->cur++; 48 | } 49 | 50 | int stopGPUTimer(GPUtimer* timer, float* times, int maxTimes) 51 | { 52 | NVG_NOTUSED(times); 53 | NVG_NOTUSED(maxTimes); 54 | GLint available = 1; 55 | int n = 0; 56 | if (!timer->supported) 57 | return 0; 58 | 59 | glEndQuery(GL_TIME_ELAPSED); 60 | while (available && timer->ret <= timer->cur) { 61 | // check for results if there are any 62 | glGetQueryObjectiv(timer->queries[timer->ret % GPU_QUERY_COUNT], GL_QUERY_RESULT_AVAILABLE, &available); 63 | if (available) { 64 | /* GLuint64 timeElapsed = 0; 65 | glGetQueryObjectui64v(timer->queries[timer->ret % GPU_QUERY_COUNT], GL_QUERY_RESULT, &timeElapsed); 66 | timer->ret++; 67 | if (n < maxTimes) { 68 | times[n] = (float)((double)timeElapsed * 1e-9); 69 | n++; 70 | }*/ 71 | } 72 | } 73 | return n; 74 | } 75 | 76 | 77 | void initGraph(PerfGraph* fps, int style, const char* name) 78 | { 79 | memset(fps, 0, sizeof(PerfGraph)); 80 | fps->style = style; 81 | strncpy(fps->name, name, sizeof(fps->name)); 82 | fps->name[sizeof(fps->name)-1] = '\0'; 83 | } 84 | 85 | void updateGraph(PerfGraph* fps, float frameTime) 86 | { 87 | fps->head = (fps->head+1) % GRAPH_HISTORY_COUNT; 88 | fps->values[fps->head] = frameTime; 89 | } 90 | 91 | float getGraphAverage(PerfGraph* fps) 92 | { 93 | int i; 94 | float avg = 0; 95 | for (i = 0; i < GRAPH_HISTORY_COUNT; i++) { 96 | avg += fps->values[i]; 97 | } 98 | return avg / (float)GRAPH_HISTORY_COUNT; 99 | } 100 | 101 | void renderGraph(NVGcontext* vg, float x, float y, PerfGraph* fps) 102 | { 103 | int i; 104 | float avg, w, h; 105 | char str[64]; 106 | 107 | avg = getGraphAverage(fps); 108 | 109 | w = 200; 110 | h = 35; 111 | 112 | nvgBeginPath(vg); 113 | nvgRect(vg, x,y, w,h); 114 | nvgFillColor(vg, nvgRGBA(0,0,0,128)); 115 | nvgFill(vg); 116 | 117 | nvgBeginPath(vg); 118 | nvgMoveTo(vg, x, y+h); 119 | if (fps->style == GRAPH_RENDER_FPS) { 120 | for (i = 0; i < GRAPH_HISTORY_COUNT; i++) { 121 | float v = 1.0f / (0.00001f + fps->values[(fps->head+i) % GRAPH_HISTORY_COUNT]); 122 | float vx, vy; 123 | if (v > 80.0f) v = 80.0f; 124 | vx = x + ((float)i/(GRAPH_HISTORY_COUNT-1)) * w; 125 | vy = y + h - ((v / 80.0f) * h); 126 | nvgLineTo(vg, vx, vy); 127 | } 128 | } else if (fps->style == GRAPH_RENDER_PERCENT) { 129 | for (i = 0; i < GRAPH_HISTORY_COUNT; i++) { 130 | float v = fps->values[(fps->head+i) % GRAPH_HISTORY_COUNT] * 1.0f; 131 | float vx, vy; 132 | if (v > 100.0f) v = 100.0f; 133 | vx = x + ((float)i/(GRAPH_HISTORY_COUNT-1)) * w; 134 | vy = y + h - ((v / 100.0f) * h); 135 | nvgLineTo(vg, vx, vy); 136 | } 137 | } else { 138 | for (i = 0; i < GRAPH_HISTORY_COUNT; i++) { 139 | float v = fps->values[(fps->head+i) % GRAPH_HISTORY_COUNT] * 1000.0f; 140 | float vx, vy; 141 | if (v > 20.0f) v = 20.0f; 142 | vx = x + ((float)i/(GRAPH_HISTORY_COUNT-1)) * w; 143 | vy = y + h - ((v / 20.0f) * h); 144 | nvgLineTo(vg, vx, vy); 145 | } 146 | } 147 | nvgLineTo(vg, x+w, y+h); 148 | nvgFillColor(vg, nvgRGBA(255,192,0,128)); 149 | nvgFill(vg); 150 | 151 | nvgFontFace(vg, "sans"); 152 | 153 | if (fps->name[0] != '\0') { 154 | nvgFontSize(vg, 12.0f); 155 | nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); 156 | nvgFillColor(vg, nvgRGBA(240,240,240,192)); 157 | nvgText(vg, x+3,y+3, fps->name, NULL); 158 | } 159 | 160 | if (fps->style == GRAPH_RENDER_FPS) { 161 | nvgFontSize(vg, 15.0f); 162 | nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); 163 | nvgFillColor(vg, nvgRGBA(240,240,240,255)); 164 | sprintf(str, "%.2f FPS", 1.0f / avg); 165 | nvgText(vg, x+w-3,y+3, str, NULL); 166 | 167 | nvgFontSize(vg, 13.0f); 168 | nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_BASELINE); 169 | nvgFillColor(vg, nvgRGBA(240,240,240,160)); 170 | sprintf(str, "%.2f ms", avg * 1000.0f); 171 | nvgText(vg, x+w-3,y+h-3, str, NULL); 172 | } 173 | else if (fps->style == GRAPH_RENDER_PERCENT) { 174 | nvgFontSize(vg, 15.0f); 175 | nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); 176 | nvgFillColor(vg, nvgRGBA(240,240,240,255)); 177 | sprintf(str, "%.1f %%", avg * 1.0f); 178 | nvgText(vg, x+w-3,y+3, str, NULL); 179 | } else { 180 | nvgFontSize(vg, 15.0f); 181 | nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); 182 | nvgFillColor(vg, nvgRGBA(240,240,240,255)); 183 | sprintf(str, "%.2f ms", avg * 1000.0f); 184 | nvgText(vg, x+w-3,y+3, str, NULL); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /example/perf.h: -------------------------------------------------------------------------------- 1 | #ifndef PERF_H 2 | #define PERF_H 3 | 4 | #include "nanovg.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | enum GraphrenderStyle { 11 | GRAPH_RENDER_FPS, 12 | GRAPH_RENDER_MS, 13 | GRAPH_RENDER_PERCENT, 14 | }; 15 | 16 | #define GRAPH_HISTORY_COUNT 100 17 | struct PerfGraph { 18 | int style; 19 | char name[32]; 20 | float values[GRAPH_HISTORY_COUNT]; 21 | int head; 22 | }; 23 | typedef struct PerfGraph PerfGraph; 24 | 25 | void initGraph(PerfGraph* fps, int style, const char* name); 26 | void updateGraph(PerfGraph* fps, float frameTime); 27 | void renderGraph(NVGcontext* vg, float x, float y, PerfGraph* fps); 28 | float getGraphAverage(PerfGraph* fps); 29 | 30 | #define GPU_QUERY_COUNT 5 31 | struct GPUtimer { 32 | int supported; 33 | int cur, ret; 34 | unsigned int queries[GPU_QUERY_COUNT]; 35 | }; 36 | typedef struct GPUtimer GPUtimer; 37 | 38 | void initGPUTimer(GPUtimer* timer); 39 | void startGPUTimer(GPUtimer* timer); 40 | int stopGPUTimer(GPUtimer* timer, float* times, int maxTimes); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif // PERF_H -------------------------------------------------------------------------------- /example/screenshot-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/screenshot-01.png -------------------------------------------------------------------------------- /example/screenshot-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/nanovg/f93799c078fa11ed61c078c65a53914c8782c00b/example/screenshot-02.png -------------------------------------------------------------------------------- /example/stb_image_write.h: -------------------------------------------------------------------------------- 1 | /* stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h 2 | writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 3 | no warranty implied; use at your own risk 4 | 5 | 6 | Before including, 7 | 8 | #define STB_IMAGE_WRITE_IMPLEMENTATION 9 | 10 | in the file that you want to have the implementation. 11 | 12 | 13 | ABOUT: 14 | 15 | This header file is a library for writing images to C stdio. It could be 16 | adapted to write to memory or a general streaming interface; let me know. 17 | 18 | The PNG output is not optimal; it is 20-50% larger than the file 19 | written by a decent optimizing implementation. This library is designed 20 | for source code compactness and simplicitly, not optimal image file size 21 | or run-time performance. 22 | 23 | USAGE: 24 | 25 | There are three functions, one for each image file format: 26 | 27 | int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 28 | int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 29 | int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 30 | 31 | Each function returns 0 on failure and non-0 on success. 32 | 33 | The functions create an image file defined by the parameters. The image 34 | is a rectangle of pixels stored from left-to-right, top-to-bottom. 35 | Each pixel contains 'comp' channels of data stored interleaved with 8-bits 36 | per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is 37 | monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. 38 | The *data pointer points to the first byte of the top-left-most pixel. 39 | For PNG, "stride_in_bytes" is the distance in bytes from the first byte of 40 | a row of pixels to the first byte of the next row of pixels. 41 | 42 | PNG creates output files with the same number of components as the input. 43 | The BMP and TGA formats expand Y to RGB in the file format. BMP does not 44 | output alpha. 45 | 46 | PNG supports writing rectangles of data even when the bytes storing rows of 47 | data are not consecutive in memory (e.g. sub-rectangles of a larger image), 48 | by supplying the stride between the beginning of adjacent rows. The other 49 | formats do not. (Thus you cannot write a native-format BMP through the BMP 50 | writer, both because it is in BGR order and because it may have padding 51 | at the end of the line.) 52 | */ 53 | 54 | #ifndef INCLUDE_STB_IMAGE_WRITE_H 55 | #define INCLUDE_STB_IMAGE_WRITE_H 56 | 57 | #ifdef __cplusplus 58 | extern "C" { 59 | #endif 60 | 61 | extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 62 | extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 63 | extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | 69 | #endif//INCLUDE_STB_IMAGE_WRITE_H 70 | 71 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION 72 | 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | 79 | typedef unsigned int stbiw_uint32; 80 | typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; 81 | 82 | static void writefv(FILE *f, const char *fmt, va_list v) 83 | { 84 | while (*fmt) { 85 | switch (*fmt++) { 86 | case ' ': break; 87 | case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; } 88 | case '2': { int x = va_arg(v,int); unsigned char b[2]; 89 | b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8); 90 | fwrite(b,2,1,f); break; } 91 | case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; 92 | b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8); 93 | b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24); 94 | fwrite(b,4,1,f); break; } 95 | default: 96 | assert(0); 97 | return; 98 | } 99 | } 100 | } 101 | 102 | static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c) 103 | { 104 | unsigned char arr[3]; 105 | arr[0] = a, arr[1] = b, arr[2] = c; 106 | fwrite(arr, 3, 1, f); 107 | } 108 | 109 | static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad) 110 | { 111 | unsigned char bg[3] = { 255, 0, 255}, px[3]; 112 | stbiw_uint32 zero = 0; 113 | int i,j,k, j_end; 114 | 115 | if (y <= 0) 116 | return; 117 | 118 | if (vdir < 0) 119 | j_end = -1, j = y-1; 120 | else 121 | j_end = y, j = 0; 122 | 123 | for (; j != j_end; j += vdir) { 124 | for (i=0; i < x; ++i) { 125 | unsigned char *d = (unsigned char *) data + (j*x+i)*comp; 126 | if (write_alpha < 0) 127 | fwrite(&d[comp-1], 1, 1, f); 128 | switch (comp) { 129 | case 1: 130 | case 2: write3(f, d[0],d[0],d[0]); 131 | break; 132 | case 4: 133 | if (!write_alpha) { 134 | // composite against pink background 135 | for (k=0; k < 3; ++k) 136 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255; 137 | write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]); 138 | break; 139 | } 140 | /* FALLTHROUGH */ 141 | case 3: 142 | write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]); 143 | break; 144 | } 145 | if (write_alpha > 0) 146 | fwrite(&d[comp-1], 1, 1, f); 147 | } 148 | fwrite(&zero,scanline_pad,1,f); 149 | } 150 | } 151 | 152 | static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...) 153 | { 154 | FILE *f; 155 | if (y < 0 || x < 0) return 0; 156 | f = fopen(filename, "wb"); 157 | if (f) { 158 | va_list v; 159 | va_start(v, fmt); 160 | writefv(f, fmt, v); 161 | va_end(v); 162 | write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); 163 | fclose(f); 164 | } 165 | return f != NULL; 166 | } 167 | 168 | int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) 169 | { 170 | int pad = (-x*3) & 3; 171 | return outfile(filename,-1,-1,x,y,comp,(void *) data,0,pad, 172 | "11 4 22 4" "4 44 22 444444", 173 | 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 174 | 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header 175 | } 176 | 177 | int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) 178 | { 179 | int has_alpha = !(comp & 1); 180 | return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0, 181 | "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha); 182 | } 183 | 184 | // stretchy buffer; stbi__sbpush() == vector<>::push_back() -- stbi__sbcount() == vector<>::size() 185 | #define stbi__sbraw(a) ((int *) (a) - 2) 186 | #define stbi__sbm(a) stbi__sbraw(a)[0] 187 | #define stbi__sbn(a) stbi__sbraw(a)[1] 188 | 189 | #define stbi__sbneedgrow(a,n) ((a)==0 || stbi__sbn(a)+n >= stbi__sbm(a)) 190 | #define stbi__sbmaybegrow(a,n) (stbi__sbneedgrow(a,(n)) ? stbi__sbgrow(a,n) : 0) 191 | #define stbi__sbgrow(a,n) stbi__sbgrowf((void **) &(a), (n), sizeof(*(a))) 192 | 193 | #define stbi__sbpush(a, v) (stbi__sbmaybegrow(a,1), (a)[stbi__sbn(a)++] = (v)) 194 | #define stbi__sbcount(a) ((a) ? stbi__sbn(a) : 0) 195 | #define stbi__sbfree(a) ((a) ? free(stbi__sbraw(a)),0 : 0) 196 | 197 | static void *stbi__sbgrowf(void **arr, int increment, int itemsize) 198 | { 199 | int m = *arr ? 2*stbi__sbm(*arr)+increment : increment+1; 200 | void *p = realloc(*arr ? stbi__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); 201 | assert(p); 202 | if (p) { 203 | if (!*arr) ((int *) p)[1] = 0; 204 | *arr = (void *) ((int *) p + 2); 205 | stbi__sbm(*arr) = m; 206 | } 207 | return *arr; 208 | } 209 | 210 | static unsigned char *stbi__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) 211 | { 212 | while (*bitcount >= 8) { 213 | stbi__sbpush(data, (unsigned char) *bitbuffer); 214 | *bitbuffer >>= 8; 215 | *bitcount -= 8; 216 | } 217 | return data; 218 | } 219 | 220 | static int stbi__zlib_bitrev(int code, int codebits) 221 | { 222 | int res=0; 223 | while (codebits--) { 224 | res = (res << 1) | (code & 1); 225 | code >>= 1; 226 | } 227 | return res; 228 | } 229 | 230 | static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int limit) 231 | { 232 | int i; 233 | for (i=0; i < limit && i < 258; ++i) 234 | if (a[i] != b[i]) break; 235 | return i; 236 | } 237 | 238 | static unsigned int stbi__zhash(unsigned char *data) 239 | { 240 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); 241 | hash ^= hash << 3; 242 | hash += hash >> 5; 243 | hash ^= hash << 4; 244 | hash += hash >> 17; 245 | hash ^= hash << 25; 246 | hash += hash >> 6; 247 | return hash; 248 | } 249 | 250 | #define stbi__zlib_flush() (out = stbi__zlib_flushf(out, &bitbuf, &bitcount)) 251 | #define stbi__zlib_add(code,codebits) \ 252 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbi__zlib_flush()) 253 | #define stbi__zlib_huffa(b,c) stbi__zlib_add(stbi__zlib_bitrev(b,c),c) 254 | // default huffman tables 255 | #define stbi__zlib_huff1(n) stbi__zlib_huffa(0x30 + (n), 8) 256 | #define stbi__zlib_huff2(n) stbi__zlib_huffa(0x190 + (n)-144, 9) 257 | #define stbi__zlib_huff3(n) stbi__zlib_huffa(0 + (n)-256,7) 258 | #define stbi__zlib_huff4(n) stbi__zlib_huffa(0xc0 + (n)-280,8) 259 | #define stbi__zlib_huff(n) ((n) <= 143 ? stbi__zlib_huff1(n) : (n) <= 255 ? stbi__zlib_huff2(n) : (n) <= 279 ? stbi__zlib_huff3(n) : stbi__zlib_huff4(n)) 260 | #define stbi__zlib_huffb(n) ((n) <= 143 ? stbi__zlib_huff1(n) : stbi__zlib_huff2(n)) 261 | 262 | #define stbi__ZHASH 16384 263 | 264 | unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) 265 | { 266 | static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; 267 | static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; 268 | static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; 269 | static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; 270 | unsigned int bitbuf=0; 271 | int i,j, bitcount=0; 272 | unsigned char *out = NULL; 273 | unsigned char **hash_table[stbi__ZHASH]; // 64KB on the stack! 274 | if (quality < 5) quality = 5; 275 | 276 | stbi__sbpush(out, 0x78); // DEFLATE 32K window 277 | stbi__sbpush(out, 0x5e); // FLEVEL = 1 278 | stbi__zlib_add(1,1); // BFINAL = 1 279 | stbi__zlib_add(1,2); // BTYPE = 1 -- fixed huffman 280 | 281 | for (i=0; i < stbi__ZHASH; ++i) 282 | hash_table[i] = NULL; 283 | 284 | i=0; 285 | while (i < data_len-3) { 286 | // hash next 3 bytes of data to be compressed 287 | int h = stbi__zhash(data+i)&(stbi__ZHASH-1), best=3; 288 | unsigned char *bestloc = 0; 289 | unsigned char **hlist = hash_table[h]; 290 | int n = stbi__sbcount(hlist); 291 | for (j=0; j < n; ++j) { 292 | if (hlist[j]-data > i-32768) { // if entry lies within window 293 | int d = stbi__zlib_countm(hlist[j], data+i, data_len-i); 294 | if (d >= best) best=d,bestloc=hlist[j]; 295 | } 296 | } 297 | // when hash table entry is too long, delete half the entries 298 | if (hash_table[h] && stbi__sbn(hash_table[h]) == 2*quality) { 299 | memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); 300 | stbi__sbn(hash_table[h]) = quality; 301 | } 302 | stbi__sbpush(hash_table[h],data+i); 303 | 304 | if (bestloc) { 305 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal 306 | h = stbi__zhash(data+i+1)&(stbi__ZHASH-1); 307 | hlist = hash_table[h]; 308 | n = stbi__sbcount(hlist); 309 | for (j=0; j < n; ++j) { 310 | if (hlist[j]-data > i-32767) { 311 | int e = stbi__zlib_countm(hlist[j], data+i+1, data_len-i-1); 312 | if (e > best) { // if next match is better, bail on current match 313 | bestloc = NULL; 314 | break; 315 | } 316 | } 317 | } 318 | } 319 | 320 | if (bestloc) { 321 | int d = data+i - bestloc; // distance back 322 | assert(d <= 32767 && best <= 258); 323 | for (j=0; best > lengthc[j+1]-1; ++j); 324 | stbi__zlib_huff(j+257); 325 | if (lengtheb[j]) stbi__zlib_add(best - lengthc[j], lengtheb[j]); 326 | for (j=0; d > distc[j+1]-1; ++j); 327 | stbi__zlib_add(stbi__zlib_bitrev(j,5),5); 328 | if (disteb[j]) stbi__zlib_add(d - distc[j], disteb[j]); 329 | i += best; 330 | } else { 331 | stbi__zlib_huffb(data[i]); 332 | ++i; 333 | } 334 | } 335 | // write out final bytes 336 | for (;i < data_len; ++i) 337 | stbi__zlib_huffb(data[i]); 338 | stbi__zlib_huff(256); // end of block 339 | // pad with 0 bits to byte boundary 340 | while (bitcount) 341 | stbi__zlib_add(0,1); 342 | 343 | for (i=0; i < stbi__ZHASH; ++i) 344 | (void) stbi__sbfree(hash_table[i]); 345 | 346 | { 347 | // compute adler32 on input 348 | unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552; 349 | int j=0; 350 | while (j < data_len) { 351 | for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; 352 | s1 %= 65521, s2 %= 65521; 353 | j += blocklen; 354 | blocklen = 5552; 355 | } 356 | stbi__sbpush(out, (unsigned char) (s2 >> 8)); 357 | stbi__sbpush(out, (unsigned char) s2); 358 | stbi__sbpush(out, (unsigned char) (s1 >> 8)); 359 | stbi__sbpush(out, (unsigned char) s1); 360 | } 361 | *out_len = stbi__sbn(out); 362 | // make returned pointer freeable 363 | memmove(stbi__sbraw(out), out, *out_len); 364 | return (unsigned char *) stbi__sbraw(out); 365 | } 366 | 367 | unsigned int stbi__crc32(unsigned char *buffer, int len) 368 | { 369 | static unsigned int crc_table[256]; 370 | unsigned int crc = ~0u; 371 | int i,j; 372 | if (crc_table[1] == 0) 373 | for(i=0; i < 256; i++) 374 | for (crc_table[i]=i, j=0; j < 8; ++j) 375 | crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0); 376 | for (i=0; i < len; ++i) 377 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; 378 | return ~crc; 379 | } 380 | 381 | #define stbi__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4) 382 | #define stbi__wp32(data,v) stbi__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); 383 | #define stbi__wptag(data,s) stbi__wpng4(data, s[0],s[1],s[2],s[3]) 384 | 385 | static void stbi__wpcrc(unsigned char **data, int len) 386 | { 387 | unsigned int crc = stbi__crc32(*data - len - 4, len+4); 388 | stbi__wp32(*data, crc); 389 | } 390 | 391 | static unsigned char stbi__paeth(int a, int b, int c) 392 | { 393 | int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); 394 | if (pa <= pb && pa <= pc) return (unsigned char) a; 395 | if (pb <= pc) return (unsigned char) b; 396 | return (unsigned char) c; 397 | } 398 | 399 | unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) 400 | { 401 | int ctype[5] = { -1, 0, 4, 2, 6 }; 402 | unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; 403 | unsigned char *out,*o, *filt, *zlib; 404 | signed char *line_buffer; 405 | int i,j,k,p,zlen; 406 | 407 | if (stride_bytes == 0) 408 | stride_bytes = x * n; 409 | 410 | filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0; 411 | line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; } 412 | for (j=0; j < y; ++j) { 413 | static int mapping[] = { 0,1,2,3,4 }; 414 | static int firstmap[] = { 0,1,0,5,6 }; 415 | int *mymap = j ? mapping : firstmap; 416 | int best = 0, bestval = 0x7fffffff; 417 | for (p=0; p < 2; ++p) { 418 | for (k= p?best:0; k < 5; ++k) { 419 | int type = mymap[k],est=0; 420 | unsigned char *z = pixels + stride_bytes*j; 421 | for (i=0; i < n; ++i) 422 | switch (type) { 423 | case 0: line_buffer[i] = z[i]; break; 424 | case 1: line_buffer[i] = z[i]; break; 425 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 426 | case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; 427 | case 4: line_buffer[i] = (signed char) (z[i] - stbi__paeth(0,z[i-stride_bytes],0)); break; 428 | case 5: line_buffer[i] = z[i]; break; 429 | case 6: line_buffer[i] = z[i]; break; 430 | } 431 | for (i=n; i < x*n; ++i) { 432 | switch (type) { 433 | case 0: line_buffer[i] = z[i]; break; 434 | case 1: line_buffer[i] = z[i] - z[i-n]; break; 435 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 436 | case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; 437 | case 4: line_buffer[i] = z[i] - stbi__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; 438 | case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; 439 | case 6: line_buffer[i] = z[i] - stbi__paeth(z[i-n], 0,0); break; 440 | } 441 | } 442 | if (p) break; 443 | for (i=0; i < x*n; ++i) 444 | est += abs((signed char) line_buffer[i]); 445 | if (est < bestval) { bestval = est; best = k; } 446 | } 447 | } 448 | // when we get here, best contains the filter type, and line_buffer contains the data 449 | filt[j*(x*n+1)] = (unsigned char) best; 450 | memcpy(filt+j*(x*n+1)+1, line_buffer, x*n); 451 | } 452 | free(line_buffer); 453 | zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory 454 | free(filt); 455 | if (!zlib) return 0; 456 | 457 | // each tag requires 12 bytes of overhead 458 | out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12); 459 | if (!out) return 0; 460 | *out_len = 8 + 12+13 + 12+zlen + 12; 461 | 462 | o=out; 463 | memcpy(o,sig,8); o+= 8; 464 | stbi__wp32(o, 13); // header length 465 | stbi__wptag(o, "IHDR"); 466 | stbi__wp32(o, x); 467 | stbi__wp32(o, y); 468 | *o++ = 8; 469 | *o++ = (unsigned char) ctype[n]; 470 | *o++ = 0; 471 | *o++ = 0; 472 | *o++ = 0; 473 | stbi__wpcrc(&o,13); 474 | 475 | stbi__wp32(o, zlen); 476 | stbi__wptag(o, "IDAT"); 477 | memcpy(o, zlib, zlen); o += zlen; free(zlib); 478 | stbi__wpcrc(&o, zlen); 479 | 480 | stbi__wp32(o,0); 481 | stbi__wptag(o, "IEND"); 482 | stbi__wpcrc(&o,0); 483 | 484 | assert(o == out + *out_len); 485 | 486 | return out; 487 | } 488 | 489 | int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) 490 | { 491 | FILE *f; 492 | int len; 493 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 494 | if (!png) return 0; 495 | f = fopen(filename, "wb"); 496 | if (!f) { free(png); return 0; } 497 | fwrite(png, 1, len, f); 498 | fclose(f); 499 | free(png); 500 | return 1; 501 | } 502 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION 503 | 504 | /* Revision history 505 | 506 | 0.92 (2010-08-01) 507 | casts to unsigned char to fix warnings 508 | 0.91 (2010-07-17) 509 | first public release 510 | 0.90 first internal release 511 | */ 512 | -------------------------------------------------------------------------------- /obsolete/nanovg_gl2.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | #ifndef NANOVG_GL2_H 19 | #define NANOVG_GL2_H 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | #define NVG_ANTIALIAS 1 26 | 27 | #ifdef NANOVG_GLES2_IMPLEMENTATION 28 | # ifndef NANOVG_GLES2 29 | # define NANOVG_GLES2 30 | # endif 31 | # ifndef NANOVG_GL2_IMPLEMENTATION 32 | # define NANOVG_GL2_IMPLEMENTATION 33 | # endif 34 | #endif 35 | 36 | #ifdef NANOVG_GLES2 37 | 38 | struct NVGcontext* nvgCreateGLES2(int atlasw, int atlash, int edgeaa); 39 | void nvgDeleteGLES2(struct NVGcontext* ctx); 40 | 41 | #else 42 | 43 | struct NVGcontext* nvgCreateGL2(int atlasw, int atlash, int edgeaa); 44 | void nvgDeleteGL2(struct NVGcontext* ctx); 45 | 46 | #endif 47 | 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | 53 | #endif 54 | 55 | #ifdef NANOVG_GL2_IMPLEMENTATION 56 | 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include "nanovg.h" 62 | 63 | enum GLNVGuniformLoc { 64 | GLNVG_LOC_VIEWSIZE, 65 | GLNVG_LOC_SCISSORMAT, 66 | GLNVG_LOC_SCISSOREXT, 67 | GLNVG_LOC_SCISSORSCALE, 68 | GLNVG_LOC_PAINTMAT, 69 | GLNVG_LOC_EXTENT, 70 | GLNVG_LOC_RADIUS, 71 | GLNVG_LOC_FEATHER, 72 | GLNVG_LOC_INNERCOL, 73 | GLNVG_LOC_OUTERCOL, 74 | GLNVG_LOC_STROKEMULT, 75 | GLNVG_LOC_TEX, 76 | GLNVG_LOC_TEXTYPE, 77 | GLNVG_LOC_TYPE, 78 | GLNVG_MAX_LOCS 79 | }; 80 | 81 | enum GLNVGshaderType { 82 | NSVG_SHADER_FILLGRAD, 83 | NSVG_SHADER_FILLIMG, 84 | NSVG_SHADER_SIMPLE, 85 | NSVG_SHADER_IMG 86 | }; 87 | 88 | struct GLNVGshader { 89 | GLuint prog; 90 | GLuint frag; 91 | GLuint vert; 92 | GLint loc[GLNVG_MAX_LOCS]; 93 | }; 94 | 95 | struct GLNVGtexture { 96 | int id; 97 | GLuint tex; 98 | int width, height; 99 | int type; 100 | }; 101 | 102 | struct GLNVGcontext { 103 | struct GLNVGshader shader; 104 | struct GLNVGtexture* textures; 105 | float viewWidth, viewHeight; 106 | int ntextures; 107 | int ctextures; 108 | int textureId; 109 | GLuint vertBuf; 110 | int edgeAntiAlias; 111 | }; 112 | 113 | static struct GLNVGtexture* glnvg__allocTexture(struct GLNVGcontext* gl) 114 | { 115 | struct GLNVGtexture* tex = NULL; 116 | int i; 117 | 118 | for (i = 0; i < gl->ntextures; i++) { 119 | if (gl->textures[i].id == 0) { 120 | tex = &gl->textures[i]; 121 | break; 122 | } 123 | } 124 | if (tex == NULL) { 125 | if (gl->ntextures+1 > gl->ctextures) { 126 | gl->ctextures = (gl->ctextures == 0) ? 2 : gl->ctextures*2; 127 | gl->textures = (struct GLNVGtexture*)realloc(gl->textures, sizeof(struct GLNVGtexture)*gl->ctextures); 128 | if (gl->textures == NULL) return NULL; 129 | } 130 | tex = &gl->textures[gl->ntextures++]; 131 | } 132 | 133 | memset(tex, 0, sizeof(*tex)); 134 | tex->id = ++gl->textureId; 135 | 136 | return tex; 137 | } 138 | 139 | static struct GLNVGtexture* glnvg__findTexture(struct GLNVGcontext* gl, int id) 140 | { 141 | int i; 142 | for (i = 0; i < gl->ntextures; i++) 143 | if (gl->textures[i].id == id) 144 | return &gl->textures[i]; 145 | return NULL; 146 | } 147 | 148 | static int glnvg__deleteTexture(struct GLNVGcontext* gl, int id) 149 | { 150 | int i; 151 | for (i = 0; i < gl->ntextures; i++) { 152 | if (gl->textures[i].id == id) { 153 | if (gl->textures[i].tex != 0) 154 | glDeleteTextures(1, &gl->textures[i].tex); 155 | memset(&gl->textures[i], 0, sizeof(gl->textures[i])); 156 | return 1; 157 | } 158 | } 159 | return 0; 160 | } 161 | 162 | static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) 163 | { 164 | char str[512+1]; 165 | int len = 0; 166 | glGetShaderInfoLog(shader, 512, &len, str); 167 | if (len > 512) len = 512; 168 | str[len] = '\0'; 169 | printf("Shader %s/%s error:\n%s\n", name, type, str); 170 | } 171 | 172 | static void glnvg__dumpProgramError(GLuint prog, const char* name) 173 | { 174 | char str[512+1]; 175 | int len = 0; 176 | glGetProgramInfoLog(prog, 512, &len, str); 177 | if (len > 512) len = 512; 178 | str[len] = '\0'; 179 | printf("Program %s error:\n%s\n", name, str); 180 | } 181 | 182 | static int glnvg__checkError(const char* str) 183 | { 184 | GLenum err = glGetError(); 185 | if (err != GL_NO_ERROR) { 186 | printf("Error %08x after %s\n", err, str); 187 | return 1; 188 | } 189 | return 0; 190 | } 191 | 192 | static int glnvg__createShader(struct GLNVGshader* shader, const char* name, const char* vshader, const char* fshader) 193 | { 194 | GLint status; 195 | GLuint prog, vert, frag; 196 | 197 | memset(shader, 0, sizeof(*shader)); 198 | 199 | prog = glCreateProgram(); 200 | vert = glCreateShader(GL_VERTEX_SHADER); 201 | frag = glCreateShader(GL_FRAGMENT_SHADER); 202 | glShaderSource(vert, 1, &vshader, 0); 203 | glShaderSource(frag, 1, &fshader, 0); 204 | 205 | glCompileShader(vert); 206 | glGetShaderiv(vert, GL_COMPILE_STATUS, &status); 207 | if (status != GL_TRUE) { 208 | glnvg__dumpShaderError(vert, name, "vert"); 209 | return 0; 210 | } 211 | 212 | glCompileShader(frag); 213 | glGetShaderiv(frag, GL_COMPILE_STATUS, &status); 214 | if (status != GL_TRUE) { 215 | glnvg__dumpShaderError(frag, name, "frag"); 216 | return 0; 217 | } 218 | 219 | glAttachShader(prog, vert); 220 | glAttachShader(prog, frag); 221 | 222 | glBindAttribLocation(prog, 0, "vertex"); 223 | glBindAttribLocation(prog, 1, "tcoord"); 224 | glBindAttribLocation(prog, 2, "color"); 225 | 226 | glLinkProgram(prog); 227 | glGetProgramiv(prog, GL_LINK_STATUS, &status); 228 | if (status != GL_TRUE) { 229 | glnvg__dumpProgramError(prog, name); 230 | return 0; 231 | } 232 | 233 | shader->prog = prog; 234 | shader->vert = vert; 235 | shader->frag = frag; 236 | 237 | return 1; 238 | } 239 | 240 | static void glnvg__deleteShader(struct GLNVGshader* shader) 241 | { 242 | if (shader->prog != 0) 243 | glDeleteProgram(shader->prog); 244 | if (shader->vert != 0) 245 | glDeleteShader(shader->vert); 246 | if (shader->frag != 0) 247 | glDeleteShader(shader->frag); 248 | } 249 | 250 | static void glnvg__getUniforms(struct GLNVGshader* shader) 251 | { 252 | shader->loc[GLNVG_LOC_VIEWSIZE] = glGetUniformLocation(shader->prog, "viewSize"); 253 | shader->loc[GLNVG_LOC_SCISSORMAT] = glGetUniformLocation(shader->prog, "scissorMat"); 254 | shader->loc[GLNVG_LOC_SCISSOREXT] = glGetUniformLocation(shader->prog, "scissorExt"); 255 | shader->loc[GLNVG_LOC_SCISSORSCALE] = glGetUniformLocation(shader->prog, "scissorScale"); 256 | shader->loc[GLNVG_LOC_PAINTMAT] = glGetUniformLocation(shader->prog, "paintMat"); 257 | shader->loc[GLNVG_LOC_EXTENT] = glGetUniformLocation(shader->prog, "extent"); 258 | shader->loc[GLNVG_LOC_RADIUS] = glGetUniformLocation(shader->prog, "radius"); 259 | shader->loc[GLNVG_LOC_FEATHER] = glGetUniformLocation(shader->prog, "feather"); 260 | shader->loc[GLNVG_LOC_INNERCOL] = glGetUniformLocation(shader->prog, "innerCol"); 261 | shader->loc[GLNVG_LOC_OUTERCOL] = glGetUniformLocation(shader->prog, "outerCol"); 262 | shader->loc[GLNVG_LOC_STROKEMULT] = glGetUniformLocation(shader->prog, "strokeMult"); 263 | shader->loc[GLNVG_LOC_TEX] = glGetUniformLocation(shader->prog, "tex"); 264 | shader->loc[GLNVG_LOC_TEXTYPE] = glGetUniformLocation(shader->prog, "texType"); 265 | shader->loc[GLNVG_LOC_TYPE] = glGetUniformLocation(shader->prog, "type"); 266 | } 267 | 268 | static int glnvg__renderCreate(void* uptr) 269 | { 270 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 271 | 272 | static const char* fillVertShader = 273 | #ifdef NANOVG_GLES2 274 | "#version 100\n" 275 | "precision mediump float;\n" 276 | #endif 277 | "uniform vec2 viewSize;\n" 278 | "attribute vec2 vertex;\n" 279 | "attribute vec2 tcoord;\n" 280 | "attribute vec4 color;\n" 281 | "varying vec2 ftcoord;\n" 282 | "varying vec4 fcolor;\n" 283 | "varying vec2 fpos;\n" 284 | "void main(void) {\n" 285 | " ftcoord = tcoord;\n" 286 | " fcolor = color;\n" 287 | " fpos = vertex;\n" 288 | " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" 289 | "}\n"; 290 | 291 | static const char* fillFragShaderEdgeAA = 292 | #ifdef NANOVG_GLES2 293 | "#version 100\n" 294 | "precision mediump float;\n" 295 | #endif 296 | "uniform mat3 scissorMat;\n" 297 | "uniform vec2 scissorExt;\n" 298 | "uniform vec2 scissorScale;\n" 299 | "uniform mat3 paintMat;\n" 300 | "uniform vec2 extent;\n" 301 | "uniform float radius;\n" 302 | "uniform float feather;\n" 303 | "uniform vec4 innerCol;\n" 304 | "uniform vec4 outerCol;\n" 305 | "uniform float strokeMult;\n" 306 | "uniform sampler2D tex;\n" 307 | "uniform int texType;\n" 308 | "uniform int type;\n" 309 | "varying vec2 ftcoord;\n" 310 | "varying vec4 fcolor;\n" 311 | "varying vec2 fpos;\n" 312 | "\n" 313 | "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" 314 | " vec2 ext2 = ext - vec2(rad,rad);\n" 315 | " vec2 d = abs(pt) - ext2;\n" 316 | " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" 317 | "}\n" 318 | "\n" 319 | "// Scissoring\n" 320 | "float scissorMask(vec2 p) {\n" 321 | " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" 322 | " sc = vec2(0.5,0.5) - sc * scissorScale;\n" 323 | " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" 324 | "}\n" 325 | "\n" 326 | "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" 327 | "float strokeMask() {\n" 328 | " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * ftcoord.y;\n" 329 | "}\n" 330 | "\n" 331 | "void main(void) {\n" 332 | " if (type == 0) {\n" 333 | " float scissor = scissorMask(fpos);\n" 334 | " float strokeAlpha = strokeMask();\n" 335 | " // Calculate gradient color using box gradient\n" 336 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" 337 | " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" 338 | " vec4 color = mix(innerCol,outerCol,d);\n" 339 | " // Combine alpha\n" 340 | " color.w *= strokeAlpha * scissor;\n" 341 | " gl_FragColor = color;\n" 342 | " } else if (type == 1) {\n" 343 | " float scissor = scissorMask(fpos);\n" 344 | " float strokeAlpha = strokeMask();\n" 345 | " // Calculate color fron texture\n" 346 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" 347 | " vec4 color = texture2D(tex, pt);\n" 348 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 349 | " // Combine alpha\n" 350 | " color.w *= strokeAlpha * scissor;\n" 351 | " gl_FragColor = color;\n" 352 | " } else if (type == 2) {\n" 353 | " gl_FragColor = vec4(1,1,1,1);\n" 354 | " } else if (type == 3) {\n" 355 | " vec4 color = texture2D(tex, ftcoord);\n" 356 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 357 | " gl_FragColor = color * fcolor;\n" 358 | " }\n" 359 | "}\n"; 360 | 361 | static const char* fillFragShader = 362 | #ifdef NANOVG_GLES2 363 | "#version 100\n" 364 | "precision mediump float;\n" 365 | #endif 366 | "uniform mat3 scissorMat;\n" 367 | "uniform vec2 scissorExt;\n" 368 | "uniform vec2 scissorScale;\n" 369 | "uniform mat3 paintMat;\n" 370 | "uniform vec2 extent;\n" 371 | "uniform float radius;\n" 372 | "uniform float feather;\n" 373 | "uniform vec4 innerCol;\n" 374 | "uniform vec4 outerCol;\n" 375 | "uniform float strokeMult;\n" 376 | "uniform sampler2D tex;\n" 377 | "uniform int texType;\n" 378 | "uniform int type;\n" 379 | "varying vec2 ftcoord;\n" 380 | "varying vec4 fcolor;\n" 381 | "varying vec2 fpos;\n" 382 | "\n" 383 | "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" 384 | " vec2 ext2 = ext - vec2(rad,rad);\n" 385 | " vec2 d = abs(pt) - ext2;\n" 386 | " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" 387 | "}\n" 388 | "\n" 389 | "// Scissoring\n" 390 | "float scissorMask(vec2 p) {\n" 391 | " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" 392 | " sc = vec2(0.5,0.5) - sc * scissorScale;\n" 393 | " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" 394 | "}\n" 395 | "\n" 396 | "void main(void) {\n" 397 | " if (type == 0) {\n" 398 | " float scissor = scissorMask(fpos);\n" 399 | " // Calculate gradient color using box gradient\n" 400 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" 401 | " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" 402 | " vec4 color = mix(innerCol,outerCol,d);\n" 403 | " // Combine alpha\n" 404 | " color.w *= scissor;\n" 405 | " gl_FragColor = color;\n" 406 | " } else if (type == 1) {\n" 407 | " float scissor = scissorMask(fpos);\n" 408 | " // Calculate color fron texture\n" 409 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" 410 | " vec4 color = texture2D(tex, pt);\n" 411 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 412 | " // Combine alpha\n" 413 | " color.w *= scissor;\n" 414 | " gl_FragColor = color;\n" 415 | " } else if (type == 2) {\n" 416 | " gl_FragColor = vec4(1,1,1,1);\n" 417 | " } else if (type == 3) {\n" 418 | " vec4 color = texture2D(tex, ftcoord);\n" 419 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 420 | " gl_FragColor = color * fcolor;\n" 421 | " }\n" 422 | "}\n"; 423 | 424 | glnvg__checkError("init"); 425 | 426 | if (gl->edgeAntiAlias) { 427 | if (glnvg__createShader(&gl->shader, "shader", fillVertShader, fillFragShaderEdgeAA) == 0) 428 | return 0; 429 | } else { 430 | if (glnvg__createShader(&gl->shader, "shader", fillVertShader, fillFragShader) == 0) 431 | return 0; 432 | } 433 | 434 | glnvg__checkError("uniform locations"); 435 | glnvg__getUniforms(&gl->shader); 436 | 437 | // Create dynamic vertex array 438 | glGenBuffers(1, &gl->vertBuf); 439 | 440 | glnvg__checkError("done"); 441 | 442 | return 1; 443 | } 444 | 445 | static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, const unsigned char* data) 446 | { 447 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 448 | struct GLNVGtexture* tex = glnvg__allocTexture(gl); 449 | if (tex == NULL) return 0; 450 | glGenTextures(1, &tex->tex); 451 | tex->width = w; 452 | tex->height = h; 453 | tex->type = type; 454 | glBindTexture(GL_TEXTURE_2D, tex->tex); 455 | 456 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 457 | 458 | if (type == NVG_TEXTURE_RGBA) 459 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 460 | else 461 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); 462 | 463 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 464 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 465 | 466 | if (glnvg__checkError("create tex")) 467 | return 0; 468 | 469 | return tex->id; 470 | } 471 | 472 | static int glnvg__renderDeleteTexture(void* uptr, int image) 473 | { 474 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 475 | return glnvg__deleteTexture(gl, image); 476 | } 477 | 478 | static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) 479 | { 480 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 481 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 482 | 483 | if (tex == NULL) return 0; 484 | glBindTexture(GL_TEXTURE_2D, tex->tex); 485 | 486 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 487 | #ifdef NANOVG_GLES2 488 | // No support for all of unpack, need to update a whole row at a time. 489 | if (tex->type == NVG_TEXTURE_RGBA) 490 | data += y*tex->width*4; 491 | else 492 | data += y*tex->width; 493 | x = 0; 494 | w = tex->width; 495 | #else 496 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); 497 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); 498 | glPixelStorei(GL_UNPACK_SKIP_ROWS, y); 499 | #endif 500 | 501 | if (tex->type == NVG_TEXTURE_RGBA) 502 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); 503 | else 504 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); 505 | 506 | return 1; 507 | } 508 | 509 | static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) 510 | { 511 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 512 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 513 | if (tex == NULL) return 0; 514 | *w = tex->width; 515 | *h = tex->height; 516 | return 1; 517 | } 518 | 519 | static void glnvg__xformIdentity(float* t) 520 | { 521 | t[0] = 1.0f; t[1] = 0.0f; 522 | t[2] = 0.0f; t[3] = 1.0f; 523 | t[4] = 0.0f; t[5] = 0.0f; 524 | } 525 | 526 | static void glnvg__xformInverse(float* inv, float* t) 527 | { 528 | double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; 529 | if (det > -1e-6 && det < 1e-6) { 530 | glnvg__xformIdentity(t); 531 | return; 532 | } 533 | invdet = 1.0 / det; 534 | inv[0] = (float)(t[3] * invdet); 535 | inv[2] = (float)(-t[2] * invdet); 536 | inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); 537 | inv[1] = (float)(-t[1] * invdet); 538 | inv[3] = (float)(t[0] * invdet); 539 | inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); 540 | } 541 | 542 | static void glnvg__xformToMat3x3(float* m3, float* t) 543 | { 544 | m3[0] = t[0]; 545 | m3[1] = t[1]; 546 | m3[2] = 0.0f; 547 | m3[3] = t[2]; 548 | m3[4] = t[3]; 549 | m3[5] = 0.0f; 550 | m3[6] = t[4]; 551 | m3[7] = t[5]; 552 | m3[8] = 1.0f; 553 | } 554 | 555 | static int glnvg__setupPaint(struct GLNVGcontext* gl, struct NVGpaint* paint, struct NVGscissor* scissor, float width, float fringe) 556 | { 557 | struct NVGcolor innerCol; 558 | struct NVGcolor outerCol; 559 | struct GLNVGtexture* tex = NULL; 560 | float invxform[6], paintMat[9], scissorMat[9]; 561 | float scissorx = 0, scissory = 0; 562 | float scissorsx = 0, scissorsy = 0; 563 | 564 | innerCol = paint->innerColor; 565 | outerCol = paint->outerColor; 566 | 567 | glnvg__xformInverse(invxform, paint->xform); 568 | glnvg__xformToMat3x3(paintMat, invxform); 569 | 570 | if (scissor->extent[0] < 0.5f || scissor->extent[1] < 0.5f) { 571 | memset(scissorMat, 0, sizeof(scissorMat)); 572 | scissorx = 1.0f; 573 | scissory = 1.0f; 574 | scissorsx = 1.0f; 575 | scissorsy = 1.0f; 576 | } else { 577 | glnvg__xformInverse(invxform, scissor->xform); 578 | glnvg__xformToMat3x3(scissorMat, invxform); 579 | scissorx = scissor->extent[0]; 580 | scissory = scissor->extent[1]; 581 | scissorsx = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe; 582 | scissorsy = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe; 583 | } 584 | 585 | if (paint->image != 0) { 586 | tex = glnvg__findTexture(gl, paint->image); 587 | if (tex == NULL) return 0; 588 | glUseProgram(gl->shader.prog); 589 | glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], NSVG_SHADER_FILLIMG); 590 | glUniform2f(gl->shader.loc[GLNVG_LOC_VIEWSIZE], gl->viewWidth, gl->viewHeight); 591 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_SCISSORMAT], 1, GL_FALSE, scissorMat); 592 | glUniform2f(gl->shader.loc[GLNVG_LOC_SCISSOREXT], scissorx, scissory); 593 | glUniform2f(gl->shader.loc[GLNVG_LOC_SCISSORSCALE], scissorsx, scissorsy); 594 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_PAINTMAT], 1, GL_FALSE, paintMat); 595 | glUniform2f(gl->shader.loc[GLNVG_LOC_EXTENT], paint->extent[0], paint->extent[1]); 596 | glUniform1f(gl->shader.loc[GLNVG_LOC_STROKEMULT], (width*0.5f + fringe*0.5f)/fringe); 597 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); 598 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEXTYPE], tex->type == NVG_TEXTURE_RGBA ? 0 : 1); 599 | glnvg__checkError("tex paint loc"); 600 | glBindTexture(GL_TEXTURE_2D, tex->tex); 601 | glnvg__checkError("tex paint tex"); 602 | } else { 603 | glUseProgram(gl->shader.prog); 604 | glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], NSVG_SHADER_FILLGRAD); 605 | glUniform2f(gl->shader.loc[GLNVG_LOC_VIEWSIZE], gl->viewWidth, gl->viewHeight); 606 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_SCISSORMAT], 1, GL_FALSE, scissorMat); 607 | glUniform2f(gl->shader.loc[GLNVG_LOC_SCISSOREXT], scissorx, scissory); 608 | glUniform2f(gl->shader.loc[GLNVG_LOC_SCISSORSCALE], scissorsx, scissorsy); 609 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_PAINTMAT], 1, GL_FALSE, paintMat); 610 | glUniform2f(gl->shader.loc[GLNVG_LOC_EXTENT], paint->extent[0], paint->extent[1]); 611 | glUniform1f(gl->shader.loc[GLNVG_LOC_RADIUS], paint->radius); 612 | glUniform1f(gl->shader.loc[GLNVG_LOC_FEATHER], paint->feather); 613 | glUniform4fv(gl->shader.loc[GLNVG_LOC_INNERCOL], 1, innerCol.rgba); 614 | glUniform4fv(gl->shader.loc[GLNVG_LOC_OUTERCOL], 1, outerCol.rgba); 615 | glUniform1f(gl->shader.loc[GLNVG_LOC_STROKEMULT], (width*0.5f + fringe*0.5f)/fringe); 616 | glnvg__checkError("grad paint loc"); 617 | } 618 | return 1; 619 | } 620 | 621 | static void glnvg__renderViewport(void* uptr, int width, int height, int alphaBlend) 622 | { 623 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 624 | gl->viewWidth = (float)width; 625 | gl->viewHeight = (float)height; 626 | 627 | glEnable(GL_BLEND); 628 | glEnable(GL_CULL_FACE); 629 | glDisable(GL_DEPTH_TEST); 630 | 631 | if (alphaBlend == NVG_PREMULTIPLIED_ALPHA) 632 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 633 | else 634 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 635 | } 636 | 637 | 638 | static void glnvg__renderFlush(void* uptr, int alphaBlend) 639 | { 640 | // struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 641 | NVG_NOTUSED(uptr); 642 | NVG_NOTUSED(alphaBlend); 643 | } 644 | 645 | static int glnvg__maxVertCount(const struct NVGpath* paths, int npaths) 646 | { 647 | int i, count = 0; 648 | for (i = 0; i < npaths; i++) { 649 | count += paths[i].nfill; 650 | count += paths[i].nstroke; 651 | } 652 | return count; 653 | } 654 | 655 | static void glnvg__uploadPaths(const struct NVGpath* paths, int npaths) 656 | { 657 | const struct NVGpath* path; 658 | int i, n = 0; 659 | for (i = 0; i < npaths; i++) { 660 | path = &paths[i]; 661 | if (path->nfill > 0) { 662 | glBufferSubData(GL_ARRAY_BUFFER, n*sizeof(struct NVGvertex), path->nfill * sizeof(struct NVGvertex), &path->fill[0].x); 663 | n += path->nfill; 664 | } 665 | if (path->nstroke > 0) { 666 | glBufferSubData(GL_ARRAY_BUFFER, n*sizeof(struct NVGvertex), path->nstroke * sizeof(struct NVGvertex), &path->stroke[0].x); 667 | n += path->nstroke; 668 | } 669 | } 670 | } 671 | 672 | static void glnvg__renderFill(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe, 673 | const float* bounds, const struct NVGpath* paths, int npaths) 674 | { 675 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 676 | const struct NVGpath* path; 677 | int i, n, offset, maxCount; 678 | 679 | if (gl->shader.prog == 0) 680 | return; 681 | 682 | maxCount = glnvg__maxVertCount(paths, npaths); 683 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 684 | glBufferData(GL_ARRAY_BUFFER, maxCount * sizeof(struct NVGvertex), NULL, GL_STREAM_DRAW); 685 | glnvg__uploadPaths(paths, npaths); 686 | 687 | if (npaths == 1 && paths[0].convex) { 688 | 689 | glEnable(GL_CULL_FACE); 690 | 691 | glEnableVertexAttribArray(0); 692 | glEnableVertexAttribArray(1); 693 | glnvg__setupPaint(gl, paint, scissor, fringe, fringe); 694 | 695 | glDisable(GL_CULL_FACE); 696 | n = 0; 697 | for (i = 0; i < npaths; i++) { 698 | path = &paths[i]; 699 | offset = n * sizeof(struct NVGvertex); 700 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 701 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(offset + 2*sizeof(float))); 702 | glDrawArrays(GL_TRIANGLE_FAN, 0, path->nfill); 703 | n += path->nfill + path->nstroke; 704 | } 705 | 706 | glEnable(GL_CULL_FACE); 707 | 708 | if (gl->edgeAntiAlias) { 709 | // Draw fringes 710 | n = 0; 711 | for (i = 0; i < npaths; i++) { 712 | path = &paths[i]; 713 | offset = (n + path->nfill) * sizeof(struct NVGvertex); 714 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 715 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(offset + 2*sizeof(float))); 716 | glDrawArrays(GL_TRIANGLE_STRIP, 0, path->nstroke); 717 | n += path->nfill + path->nstroke; 718 | } 719 | } 720 | 721 | glUseProgram(0); 722 | glDisableVertexAttribArray(0); 723 | glDisableVertexAttribArray(1); 724 | 725 | } else { 726 | float quad[6*2] = { 727 | bounds[0], bounds[3], bounds[2], bounds[3], bounds[2], bounds[1], 728 | bounds[0], bounds[3], bounds[2], bounds[1], bounds[0], bounds[1], 729 | }; 730 | 731 | glEnable(GL_CULL_FACE); 732 | 733 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 734 | 735 | // Draw shapes 736 | glDisable(GL_BLEND); 737 | glEnable(GL_STENCIL_TEST); 738 | glStencilMask(0xff); 739 | glStencilFunc(GL_ALWAYS, 0, ~0U); 740 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 741 | 742 | glUseProgram(gl->shader.prog); 743 | glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], NSVG_SHADER_SIMPLE); 744 | glUniform2f(gl->shader.loc[GLNVG_LOC_VIEWSIZE], gl->viewWidth, gl->viewHeight); 745 | glnvg__checkError("fill solid loc"); 746 | 747 | glEnableVertexAttribArray(0); 748 | 749 | glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); 750 | glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); 751 | glDisable(GL_CULL_FACE); 752 | n = 0; 753 | for (i = 0; i < npaths; i++) { 754 | path = &paths[i]; 755 | offset = n * sizeof(struct NVGvertex); 756 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 757 | glDrawArrays(GL_TRIANGLE_FAN, 0, path->nfill); 758 | n += path->nfill + path->nstroke; 759 | } 760 | 761 | glEnable(GL_CULL_FACE); 762 | 763 | // Draw aliased off-pixels 764 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 765 | glEnable(GL_BLEND); 766 | 767 | glEnableVertexAttribArray(1); 768 | glnvg__setupPaint(gl, paint, scissor, fringe, fringe); 769 | 770 | if (gl->edgeAntiAlias) { 771 | glStencilFunc(GL_EQUAL, 0x00, 0xff); 772 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 773 | 774 | // Draw fringes 775 | n = 0; 776 | for (i = 0; i < npaths; i++) { 777 | path = &paths[i]; 778 | offset = (n + path->nfill) * sizeof(struct NVGvertex); 779 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 780 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(offset + 2*sizeof(float))); 781 | glDrawArrays(GL_TRIANGLE_STRIP, 0, path->nstroke); 782 | n += path->nfill + path->nstroke; 783 | } 784 | } 785 | 786 | // Draw fill 787 | glStencilFunc(GL_NOTEQUAL, 0x0, 0xff); 788 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); 789 | 790 | glDisableVertexAttribArray(1); 791 | 792 | glBufferSubData(GL_ARRAY_BUFFER, 0, 6 * 2*sizeof(float), quad); 793 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), (const GLvoid*)0); 794 | glVertexAttrib2f(1, 0.5f, 1.0f); 795 | glDrawArrays(GL_TRIANGLES, 0, 6); 796 | 797 | glUseProgram(0); 798 | 799 | glDisableVertexAttribArray(0); 800 | 801 | glDisable(GL_STENCIL_TEST); 802 | } 803 | } 804 | 805 | static void glnvg__renderStroke(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe, 806 | float width, const struct NVGpath* paths, int npaths) 807 | { 808 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 809 | const struct NVGpath* path; 810 | int i, n, offset, maxCount; 811 | 812 | if (gl->shader.prog == 0) 813 | return; 814 | 815 | glnvg__setupPaint(gl, paint, scissor, width, fringe); 816 | 817 | glEnable(GL_CULL_FACE); 818 | 819 | maxCount = glnvg__maxVertCount(paths, npaths); 820 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 821 | glBufferData(GL_ARRAY_BUFFER, maxCount * sizeof(struct NVGvertex), NULL, GL_STREAM_DRAW); 822 | glnvg__uploadPaths(paths, npaths); 823 | 824 | glEnableVertexAttribArray(0); 825 | glEnableVertexAttribArray(1); 826 | 827 | // Draw Strokes 828 | n = 0; 829 | for (i = 0; i < npaths; i++) { 830 | path = &paths[i]; 831 | offset = (n + path->nfill) * sizeof(struct NVGvertex); 832 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 833 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(offset + 2*sizeof(float))); 834 | glDrawArrays(GL_TRIANGLE_STRIP, 0, path->nstroke); 835 | n += path->nfill + path->nstroke; 836 | } 837 | 838 | glDisableVertexAttribArray(0); 839 | glDisableVertexAttribArray(1); 840 | 841 | glUseProgram(0); 842 | } 843 | 844 | static void glnvg__renderTriangles(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, 845 | const struct NVGvertex* verts, int nverts) 846 | { 847 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 848 | struct GLNVGtexture* tex = glnvg__findTexture(gl, paint->image); 849 | struct NVGcolor color; 850 | NVG_NOTUSED(scissor); 851 | 852 | if (gl->shader.prog == 0) 853 | return; 854 | 855 | if (tex != NULL) { 856 | glBindTexture(GL_TEXTURE_2D, tex->tex); 857 | } 858 | 859 | glUseProgram(gl->shader.prog); 860 | glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], NSVG_SHADER_IMG); 861 | glUniform2f(gl->shader.loc[GLNVG_LOC_VIEWSIZE], gl->viewWidth, gl->viewHeight); 862 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); 863 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEXTYPE], (tex != NULL && tex->type == NVG_TEXTURE_RGBA) ? 0 : 1); 864 | glnvg__checkError("tris solid img loc"); 865 | 866 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 867 | glBufferData(GL_ARRAY_BUFFER, nverts * sizeof(struct NVGvertex), verts, GL_STREAM_DRAW); 868 | 869 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)0); 870 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(2 * sizeof(float))); 871 | glEnableVertexAttribArray(0); 872 | glEnableVertexAttribArray(1); 873 | 874 | color = paint->innerColor; 875 | glVertexAttrib4fv(2, color.rgba); 876 | 877 | glDrawArrays(GL_TRIANGLES, 0, nverts); 878 | 879 | glDisableVertexAttribArray(0); 880 | glDisableVertexAttribArray(1); 881 | } 882 | 883 | static void glnvg__renderDelete(void* uptr) 884 | { 885 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 886 | int i; 887 | if (gl == NULL) return; 888 | 889 | glnvg__deleteShader(&gl->shader); 890 | 891 | for (i = 0; i < gl->ntextures; i++) { 892 | if (gl->textures[i].tex != 0) 893 | glDeleteTextures(1, &gl->textures[i].tex); 894 | } 895 | free(gl->textures); 896 | 897 | free(gl); 898 | } 899 | 900 | 901 | #ifdef NANOVG_GLES2 902 | struct NVGcontext* nvgCreateGLES2(int atlasw, int atlash, int edgeaa) 903 | #else 904 | struct NVGcontext* nvgCreateGL2(int atlasw, int atlash, int edgeaa) 905 | #endif 906 | { 907 | struct NVGparams params; 908 | struct NVGcontext* ctx = NULL; 909 | struct GLNVGcontext* gl = (struct GLNVGcontext*)malloc(sizeof(struct GLNVGcontext)); 910 | if (gl == NULL) goto error; 911 | memset(gl, 0, sizeof(struct GLNVGcontext)); 912 | 913 | memset(¶ms, 0, sizeof(params)); 914 | params.renderCreate = glnvg__renderCreate; 915 | params.renderCreateTexture = glnvg__renderCreateTexture; 916 | params.renderDeleteTexture = glnvg__renderDeleteTexture; 917 | params.renderUpdateTexture = glnvg__renderUpdateTexture; 918 | params.renderGetTextureSize = glnvg__renderGetTextureSize; 919 | params.renderViewport = glnvg__renderViewport; 920 | params.renderFlush = glnvg__renderFlush; 921 | params.renderFill = glnvg__renderFill; 922 | params.renderStroke = glnvg__renderStroke; 923 | params.renderTriangles = glnvg__renderTriangles; 924 | params.renderDelete = glnvg__renderDelete; 925 | params.userPtr = gl; 926 | params.atlasWidth = atlasw; 927 | params.atlasHeight = atlash; 928 | params.edgeAntiAlias = edgeaa; 929 | 930 | gl->edgeAntiAlias = edgeaa; 931 | 932 | ctx = nvgCreateInternal(¶ms); 933 | if (ctx == NULL) goto error; 934 | 935 | return ctx; 936 | 937 | error: 938 | // 'gl' is freed by nvgDeleteInternal. 939 | if (ctx != NULL) nvgDeleteInternal(ctx); 940 | return NULL; 941 | } 942 | 943 | #ifdef NANOVG_GLES2 944 | void nvgDeleteGLES2(struct NVGcontext* ctx) 945 | #else 946 | void nvgDeleteGL2(struct NVGcontext* ctx) 947 | #endif 948 | { 949 | nvgDeleteInternal(ctx); 950 | } 951 | 952 | #endif 953 | -------------------------------------------------------------------------------- /obsolete/nanovg_gl3.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | #ifndef NANOVG_GL3_H 19 | #define NANOVG_GL3_H 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | #define NVG_ANTIALIAS 1 26 | 27 | #ifdef NANOVG_GLES3_IMPLEMENTATION 28 | # ifndef NANOVG_GLES3 29 | # define NANOVG_GLES3 30 | # endif 31 | # ifndef NANOVG_GL3_IMPLEMENTATION 32 | # define NANOVG_GL3_IMPLEMENTATION 33 | # endif 34 | #endif 35 | 36 | #ifdef NANOVG_GLES3 37 | 38 | struct NVGcontext* nvgCreateGLES3(int atlasw, int atlash, int edgeaa); 39 | void nvgDeleteGLES3(struct NVGcontext* ctx); 40 | 41 | #else 42 | 43 | struct NVGcontext* nvgCreateGL3(int atlasw, int atlash, int edgeaa); 44 | void nvgDeleteGL3(struct NVGcontext* ctx); 45 | 46 | #endif 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif 53 | 54 | #ifdef NANOVG_GL3_IMPLEMENTATION 55 | 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include "nanovg.h" 61 | 62 | enum GLNVGuniformLoc { 63 | GLNVG_LOC_VIEWSIZE, 64 | GLNVG_LOC_SCISSORMAT, 65 | GLNVG_LOC_SCISSOREXT, 66 | GLNVG_LOC_SCISSORSCALE, 67 | GLNVG_LOC_PAINTMAT, 68 | GLNVG_LOC_EXTENT, 69 | GLNVG_LOC_RADIUS, 70 | GLNVG_LOC_FEATHER, 71 | GLNVG_LOC_INNERCOL, 72 | GLNVG_LOC_OUTERCOL, 73 | GLNVG_LOC_STROKEMULT, 74 | GLNVG_LOC_TEX, 75 | GLNVG_LOC_TEXTYPE, 76 | GLNVG_LOC_TYPE, 77 | GLNVG_MAX_LOCS 78 | }; 79 | 80 | enum GLNVGshaderType { 81 | NSVG_SHADER_FILLGRAD, 82 | NSVG_SHADER_FILLIMG, 83 | NSVG_SHADER_SIMPLE, 84 | NSVG_SHADER_IMG 85 | }; 86 | 87 | struct GLNVGshader { 88 | GLuint prog; 89 | GLuint frag; 90 | GLuint vert; 91 | GLint loc[GLNVG_MAX_LOCS]; 92 | }; 93 | 94 | struct GLNVGtexture { 95 | int id; 96 | GLuint tex; 97 | int width, height; 98 | int type; 99 | }; 100 | 101 | struct GLNVGcontext { 102 | struct GLNVGshader shader; 103 | struct GLNVGtexture* textures; 104 | float viewWidth, viewHeight; 105 | int ntextures; 106 | int ctextures; 107 | int textureId; 108 | GLuint vertArr; 109 | GLuint vertBuf; 110 | int edgeAntiAlias; 111 | }; 112 | 113 | static struct GLNVGtexture* glnvg__allocTexture(struct GLNVGcontext* gl) 114 | { 115 | struct GLNVGtexture* tex = NULL; 116 | int i; 117 | 118 | for (i = 0; i < gl->ntextures; i++) { 119 | if (gl->textures[i].id == 0) { 120 | tex = &gl->textures[i]; 121 | break; 122 | } 123 | } 124 | if (tex == NULL) { 125 | if (gl->ntextures+1 > gl->ctextures) { 126 | gl->ctextures = (gl->ctextures == 0) ? 2 : gl->ctextures*2; 127 | gl->textures = (struct GLNVGtexture*)realloc(gl->textures, sizeof(struct GLNVGtexture)*gl->ctextures); 128 | if (gl->textures == NULL) return NULL; 129 | } 130 | tex = &gl->textures[gl->ntextures++]; 131 | } 132 | 133 | memset(tex, 0, sizeof(*tex)); 134 | tex->id = ++gl->textureId; 135 | 136 | return tex; 137 | } 138 | 139 | static struct GLNVGtexture* glnvg__findTexture(struct GLNVGcontext* gl, int id) 140 | { 141 | int i; 142 | for (i = 0; i < gl->ntextures; i++) 143 | if (gl->textures[i].id == id) 144 | return &gl->textures[i]; 145 | return NULL; 146 | } 147 | 148 | static int glnvg__deleteTexture(struct GLNVGcontext* gl, int id) 149 | { 150 | int i; 151 | for (i = 0; i < gl->ntextures; i++) { 152 | if (gl->textures[i].id == id) { 153 | if (gl->textures[i].tex != 0) 154 | glDeleteTextures(1, &gl->textures[i].tex); 155 | memset(&gl->textures[i], 0, sizeof(gl->textures[i])); 156 | return 1; 157 | } 158 | } 159 | return 0; 160 | } 161 | 162 | static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) 163 | { 164 | char str[512+1]; 165 | int len = 0; 166 | glGetShaderInfoLog(shader, 512, &len, str); 167 | if (len > 512) len = 512; 168 | str[len] = '\0'; 169 | printf("Shader %s/%s error:\n%s\n", name, type, str); 170 | } 171 | 172 | static void glnvg__dumpProgramError(GLuint prog, const char* name) 173 | { 174 | char str[512+1]; 175 | int len = 0; 176 | glGetProgramInfoLog(prog, 512, &len, str); 177 | if (len > 512) len = 512; 178 | str[len] = '\0'; 179 | printf("Program %s error:\n%s\n", name, str); 180 | } 181 | 182 | static int glnvg__checkError(const char* str) 183 | { 184 | GLenum err = glGetError(); 185 | if (err != GL_NO_ERROR) { 186 | printf("Error %08x after %s\n", err, str); 187 | return 1; 188 | } 189 | return 0; 190 | } 191 | 192 | static int glnvg__createShader(struct GLNVGshader* shader, const char* name, const char* vshader, const char* fshader) 193 | { 194 | GLint status; 195 | GLuint prog, vert, frag; 196 | 197 | memset(shader, 0, sizeof(*shader)); 198 | 199 | prog = glCreateProgram(); 200 | vert = glCreateShader(GL_VERTEX_SHADER); 201 | frag = glCreateShader(GL_FRAGMENT_SHADER); 202 | glShaderSource(vert, 1, &vshader, 0); 203 | glShaderSource(frag, 1, &fshader, 0); 204 | 205 | glCompileShader(vert); 206 | glGetShaderiv(vert, GL_COMPILE_STATUS, &status); 207 | if (status != GL_TRUE) { 208 | glnvg__dumpShaderError(vert, name, "vert"); 209 | return 0; 210 | } 211 | 212 | glCompileShader(frag); 213 | glGetShaderiv(frag, GL_COMPILE_STATUS, &status); 214 | if (status != GL_TRUE) { 215 | glnvg__dumpShaderError(frag, name, "frag"); 216 | return 0; 217 | } 218 | 219 | glAttachShader(prog, vert); 220 | glAttachShader(prog, frag); 221 | 222 | glBindAttribLocation(prog, 0, "vertex"); 223 | glBindAttribLocation(prog, 1, "tcoord"); 224 | glBindAttribLocation(prog, 2, "color"); 225 | 226 | glLinkProgram(prog); 227 | glGetProgramiv(prog, GL_LINK_STATUS, &status); 228 | if (status != GL_TRUE) { 229 | glnvg__dumpProgramError(prog, name); 230 | return 0; 231 | } 232 | 233 | shader->prog = prog; 234 | shader->vert = vert; 235 | shader->frag = frag; 236 | 237 | return 1; 238 | } 239 | 240 | static void glnvg__deleteShader(struct GLNVGshader* shader) 241 | { 242 | if (shader->prog != 0) 243 | glDeleteProgram(shader->prog); 244 | if (shader->vert != 0) 245 | glDeleteShader(shader->vert); 246 | if (shader->frag != 0) 247 | glDeleteShader(shader->frag); 248 | } 249 | 250 | static void glnvg__getUniforms(struct GLNVGshader* shader) 251 | { 252 | shader->loc[GLNVG_LOC_VIEWSIZE] = glGetUniformLocation(shader->prog, "viewSize"); 253 | shader->loc[GLNVG_LOC_SCISSORMAT] = glGetUniformLocation(shader->prog, "scissorMat"); 254 | shader->loc[GLNVG_LOC_SCISSOREXT] = glGetUniformLocation(shader->prog, "scissorExt"); 255 | shader->loc[GLNVG_LOC_SCISSORSCALE] = glGetUniformLocation(shader->prog, "scissorScale"); 256 | shader->loc[GLNVG_LOC_PAINTMAT] = glGetUniformLocation(shader->prog, "paintMat"); 257 | shader->loc[GLNVG_LOC_EXTENT] = glGetUniformLocation(shader->prog, "extent"); 258 | shader->loc[GLNVG_LOC_RADIUS] = glGetUniformLocation(shader->prog, "radius"); 259 | shader->loc[GLNVG_LOC_FEATHER] = glGetUniformLocation(shader->prog, "feather"); 260 | shader->loc[GLNVG_LOC_INNERCOL] = glGetUniformLocation(shader->prog, "innerCol"); 261 | shader->loc[GLNVG_LOC_OUTERCOL] = glGetUniformLocation(shader->prog, "outerCol"); 262 | shader->loc[GLNVG_LOC_STROKEMULT] = glGetUniformLocation(shader->prog, "strokeMult"); 263 | shader->loc[GLNVG_LOC_TEX] = glGetUniformLocation(shader->prog, "tex"); 264 | shader->loc[GLNVG_LOC_TEXTYPE] = glGetUniformLocation(shader->prog, "texType"); 265 | shader->loc[GLNVG_LOC_TYPE] = glGetUniformLocation(shader->prog, "type"); 266 | } 267 | 268 | static int glnvg__renderCreate(void* uptr) 269 | { 270 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 271 | 272 | static const char* fillVertShader = 273 | #ifdef NANOVG_GLES3 274 | "#version 300 es\n" 275 | "precision mediump float;\n" 276 | #else 277 | "#version 150 core\n" 278 | #endif 279 | "uniform vec2 viewSize;\n" 280 | "in vec2 vertex;\n" 281 | "in vec2 tcoord;\n" 282 | "in vec4 color;\n" 283 | "out vec2 ftcoord;\n" 284 | "out vec4 fcolor;\n" 285 | "out vec2 fpos;\n" 286 | "void main(void) {\n" 287 | " ftcoord = tcoord;\n" 288 | " fcolor = color;\n" 289 | " fpos = vertex;\n" 290 | " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" 291 | "}\n"; 292 | 293 | static const char* fillFragShaderEdgeAA = 294 | #ifdef NANOVG_GLES3 295 | "#version 300 es\n" 296 | "precision mediump float;\n" 297 | #else 298 | "#version 150 core\n" 299 | #endif 300 | "uniform mat3 scissorMat;\n" 301 | "uniform vec2 scissorExt;\n" 302 | "uniform vec2 scissorScale;\n" 303 | "uniform mat3 paintMat;\n" 304 | "uniform vec2 extent;\n" 305 | "uniform float radius;\n" 306 | "uniform float feather;\n" 307 | "uniform vec4 innerCol;\n" 308 | "uniform vec4 outerCol;\n" 309 | "uniform float strokeMult;\n" 310 | "uniform sampler2D tex;\n" 311 | "uniform int texType;\n" 312 | "uniform int type;\n" 313 | "in vec2 ftcoord;\n" 314 | "in vec4 fcolor;\n" 315 | "in vec2 fpos;\n" 316 | "out vec4 outColor;\n" 317 | "\n" 318 | "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" 319 | " vec2 ext2 = ext - vec2(rad,rad);\n" 320 | " vec2 d = abs(pt) - ext2;\n" 321 | " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" 322 | "}\n" 323 | "\n" 324 | "// Scissoring\n" 325 | "float scissorMask(vec2 p) {\n" 326 | " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" 327 | " sc = vec2(0.5,0.5) - sc * scissorScale;\n" 328 | " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" 329 | "}\n" 330 | "\n" 331 | "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" 332 | "float strokeMask() {\n" 333 | " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * ftcoord.y;\n" 334 | "}\n" 335 | "\n" 336 | "void main(void) {\n" 337 | " if (type == 0) { // Gradient\n" 338 | " float scissor = scissorMask(fpos);\n" 339 | " float strokeAlpha = strokeMask();\n" 340 | " // Calculate gradient color using box gradient\n" 341 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" 342 | " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" 343 | " vec4 color = mix(innerCol,outerCol,d);\n" 344 | " // Combine alpha\n" 345 | " color.w *= strokeAlpha * scissor;\n" 346 | " outColor = color;\n" 347 | " } else if (type == 1) { // Image\n" 348 | " float scissor = scissorMask(fpos);\n" 349 | " float strokeAlpha = strokeMask();\n" 350 | " // Calculate color fron texture\n" 351 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" 352 | " vec4 color = texture(tex, pt);\n" 353 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 354 | " // Combine alpha\n" 355 | " color.w *= strokeAlpha * scissor;\n" 356 | " outColor = color;\n" 357 | " } else if (type == 2) { // Stencil fill\n" 358 | " outColor = vec4(1,1,1,1);\n" 359 | " } else if (type == 3) { // Textured tris\n" 360 | " vec4 color = texture(tex, ftcoord);\n" 361 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 362 | " outColor = color * fcolor;\n" 363 | " }\n" 364 | "}\n"; 365 | 366 | static const char* fillFragShader = 367 | #ifdef NANOVG_GLES3 368 | "#version 300 es\n" 369 | "precision mediump float;\n" 370 | #else 371 | "#version 150 core\n" 372 | #endif 373 | "uniform mat3 scissorMat;\n" 374 | "uniform vec2 scissorExt;\n" 375 | "uniform vec2 scissorScale;\n" 376 | "uniform mat3 paintMat;\n" 377 | "uniform vec2 extent;\n" 378 | "uniform float radius;\n" 379 | "uniform float feather;\n" 380 | "uniform vec4 innerCol;\n" 381 | "uniform vec4 outerCol;\n" 382 | "uniform float strokeMult;\n" 383 | "uniform sampler2D tex;\n" 384 | "uniform int texType;\n" 385 | "uniform int type;\n" 386 | "in vec2 ftcoord;\n" 387 | "in vec4 fcolor;\n" 388 | "in vec2 fpos;\n" 389 | "out vec4 outColor;\n" 390 | "\n" 391 | "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" 392 | " vec2 ext2 = ext - vec2(rad,rad);\n" 393 | " vec2 d = abs(pt) - ext2;\n" 394 | " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" 395 | "}\n" 396 | "\n" 397 | "// Scissoring\n" 398 | "float scissorMask(vec2 p) {\n" 399 | " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" 400 | " sc = vec2(0.5,0.5) - sc * scissorScale;\n" 401 | " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" 402 | "}\n" 403 | "\n" 404 | "void main(void) {\n" 405 | " if (type == 0) { // Gradient\n" 406 | " float scissor = scissorMask(fpos);\n" 407 | " // Calculate gradient color using box gradient\n" 408 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" 409 | " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" 410 | " vec4 color = mix(innerCol,outerCol,d);\n" 411 | " // Combine alpha\n" 412 | " color.w *= scissor;\n" 413 | " outColor = color;\n" 414 | " } else if (type == 1) { // Image\n" 415 | " float scissor = scissorMask(fpos);\n" 416 | " // Calculate color fron texture\n" 417 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" 418 | " vec4 color = texture(tex, pt);\n" 419 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 420 | " // Combine alpha\n" 421 | " color.w *= scissor;\n" 422 | " outColor = color;\n" 423 | " } else if (type == 2) { // Stencil fill\n" 424 | " outColor = vec4(1,1,1,1);\n" 425 | " } else if (type == 3) { // Textured tris\n" 426 | " vec4 color = texture(tex, ftcoord);\n" 427 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 428 | " outColor = color * fcolor;\n" 429 | " }\n" 430 | "}\n"; 431 | 432 | glnvg__checkError("init"); 433 | 434 | if (gl->edgeAntiAlias) { 435 | if (glnvg__createShader(&gl->shader, "shader", fillVertShader, fillFragShaderEdgeAA) == 0) 436 | return 0; 437 | } else { 438 | if (glnvg__createShader(&gl->shader, "shader", fillVertShader, fillFragShader) == 0) 439 | return 0; 440 | } 441 | 442 | glnvg__checkError("uniform locations"); 443 | glnvg__getUniforms(&gl->shader); 444 | 445 | // Create dynamic vertex array 446 | glGenVertexArrays(1, &gl->vertArr); 447 | glGenBuffers(1, &gl->vertBuf); 448 | 449 | glnvg__checkError("done"); 450 | 451 | return 1; 452 | } 453 | 454 | static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, const unsigned char* data) 455 | { 456 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 457 | struct GLNVGtexture* tex = glnvg__allocTexture(gl); 458 | if (tex == NULL) return 0; 459 | glGenTextures(1, &tex->tex); 460 | tex->width = w; 461 | tex->height = h; 462 | tex->type = type; 463 | glBindTexture(GL_TEXTURE_2D, tex->tex); 464 | 465 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 466 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); 467 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 468 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 469 | 470 | if (type == NVG_TEXTURE_RGBA) 471 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 472 | else 473 | #ifdef NANOVG_GLES3 474 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); 475 | #else 476 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); 477 | #endif 478 | 479 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 480 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 481 | 482 | if (glnvg__checkError("create tex")) 483 | return 0; 484 | 485 | return tex->id; 486 | } 487 | 488 | static int glnvg__renderDeleteTexture(void* uptr, int image) 489 | { 490 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 491 | return glnvg__deleteTexture(gl, image); 492 | } 493 | 494 | static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) 495 | { 496 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 497 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 498 | 499 | if (tex == NULL) return 0; 500 | glBindTexture(GL_TEXTURE_2D, tex->tex); 501 | 502 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 503 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); 504 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); 505 | glPixelStorei(GL_UNPACK_SKIP_ROWS, y); 506 | 507 | if (tex->type == NVG_TEXTURE_RGBA) 508 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); 509 | else 510 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); 511 | 512 | return 1; 513 | } 514 | 515 | static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) 516 | { 517 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 518 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 519 | if (tex == NULL) return 0; 520 | *w = tex->width; 521 | *h = tex->height; 522 | return 1; 523 | } 524 | 525 | static void glnvg__xformIdentity(float* t) 526 | { 527 | t[0] = 1.0f; t[1] = 0.0f; 528 | t[2] = 0.0f; t[3] = 1.0f; 529 | t[4] = 0.0f; t[5] = 0.0f; 530 | } 531 | 532 | static void glnvg__xformInverse(float* inv, float* t) 533 | { 534 | double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; 535 | if (det > -1e-6 && det < 1e-6) { 536 | glnvg__xformIdentity(t); 537 | return; 538 | } 539 | invdet = 1.0 / det; 540 | inv[0] = (float)(t[3] * invdet); 541 | inv[2] = (float)(-t[2] * invdet); 542 | inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); 543 | inv[1] = (float)(-t[1] * invdet); 544 | inv[3] = (float)(t[0] * invdet); 545 | inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); 546 | } 547 | 548 | static void glnvg__xformToMat3x3(float* m3, float* t) 549 | { 550 | m3[0] = t[0]; 551 | m3[1] = t[1]; 552 | m3[2] = 0.0f; 553 | m3[3] = t[2]; 554 | m3[4] = t[3]; 555 | m3[5] = 0.0f; 556 | m3[6] = t[4]; 557 | m3[7] = t[5]; 558 | m3[8] = 1.0f; 559 | } 560 | 561 | static int glnvg__setupPaint(struct GLNVGcontext* gl, struct NVGpaint* paint, struct NVGscissor* scissor, float width, float fringe) 562 | { 563 | struct NVGcolor innerCol; 564 | struct NVGcolor outerCol; 565 | struct GLNVGtexture* tex = NULL; 566 | float invxform[6], paintMat[9], scissorMat[9]; 567 | float scissorx = 0, scissory = 0; 568 | float scissorsx = 0, scissorsy = 0; 569 | 570 | innerCol = paint->innerColor; 571 | outerCol = paint->outerColor; 572 | 573 | glnvg__xformInverse(invxform, paint->xform); 574 | glnvg__xformToMat3x3(paintMat, invxform); 575 | 576 | if (scissor->extent[0] < 0.5f || scissor->extent[1] < 0.5f) { 577 | memset(scissorMat, 0, sizeof(scissorMat)); 578 | scissorx = 1.0f; 579 | scissory = 1.0f; 580 | scissorsx = 1.0f; 581 | scissorsy = 1.0f; 582 | } else { 583 | glnvg__xformInverse(invxform, scissor->xform); 584 | glnvg__xformToMat3x3(scissorMat, invxform); 585 | scissorx = scissor->extent[0]; 586 | scissory = scissor->extent[1]; 587 | scissorsx = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe; 588 | scissorsy = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe; 589 | } 590 | 591 | if (paint->image != 0) { 592 | tex = glnvg__findTexture(gl, paint->image); 593 | if (tex == NULL) return 0; 594 | glUseProgram(gl->shader.prog); 595 | glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], NSVG_SHADER_FILLIMG); 596 | glUniform2f(gl->shader.loc[GLNVG_LOC_VIEWSIZE], gl->viewWidth, gl->viewHeight); 597 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_SCISSORMAT], 1, GL_FALSE, scissorMat); 598 | glUniform2f(gl->shader.loc[GLNVG_LOC_SCISSOREXT], scissorx, scissory); 599 | glUniform2f(gl->shader.loc[GLNVG_LOC_SCISSORSCALE], scissorsx, scissorsy); 600 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_PAINTMAT], 1, GL_FALSE, paintMat); 601 | glUniform2f(gl->shader.loc[GLNVG_LOC_EXTENT], paint->extent[0], paint->extent[1]); 602 | glUniform1f(gl->shader.loc[GLNVG_LOC_STROKEMULT], (width*0.5f + fringe*0.5f)/fringe); 603 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); 604 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEXTYPE], tex->type == NVG_TEXTURE_RGBA ? 0 : 1); 605 | glnvg__checkError("tex paint loc"); 606 | glBindTexture(GL_TEXTURE_2D, tex->tex); 607 | glnvg__checkError("tex paint tex"); 608 | } else { 609 | glUseProgram(gl->shader.prog); 610 | glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], NSVG_SHADER_FILLGRAD); 611 | glUniform2f(gl->shader.loc[GLNVG_LOC_VIEWSIZE], gl->viewWidth, gl->viewHeight); 612 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_SCISSORMAT], 1, GL_FALSE, scissorMat); 613 | glUniform2f(gl->shader.loc[GLNVG_LOC_SCISSOREXT], scissorx, scissory); 614 | glUniform2f(gl->shader.loc[GLNVG_LOC_SCISSORSCALE], scissorsx, scissorsy); 615 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_PAINTMAT], 1, GL_FALSE, paintMat); 616 | glUniform2f(gl->shader.loc[GLNVG_LOC_EXTENT], paint->extent[0], paint->extent[1]); 617 | glUniform1f(gl->shader.loc[GLNVG_LOC_RADIUS], paint->radius); 618 | glUniform1f(gl->shader.loc[GLNVG_LOC_FEATHER], paint->feather); 619 | glUniform4fv(gl->shader.loc[GLNVG_LOC_INNERCOL], 1, innerCol.rgba); 620 | glUniform4fv(gl->shader.loc[GLNVG_LOC_OUTERCOL], 1, outerCol.rgba); 621 | glUniform1f(gl->shader.loc[GLNVG_LOC_STROKEMULT], (width*0.5f + fringe*0.5f)/fringe); 622 | glnvg__checkError("grad paint loc"); 623 | } 624 | return 1; 625 | } 626 | 627 | static void glnvg__renderViewport(void* uptr, int width, int height, int alphaBlend) 628 | { 629 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 630 | gl->viewWidth = (float)width; 631 | gl->viewHeight = (float)height; 632 | 633 | if (alphaBlend == NVG_PREMULTIPLIED_ALPHA) 634 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 635 | else 636 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 637 | } 638 | 639 | static void glnvg__renderFlush(void* uptr, int alphaBlend) 640 | { 641 | // struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 642 | NVG_NOTUSED(uptr); 643 | NVG_NOTUSED(alphaBlend); 644 | } 645 | 646 | static int glnvg__maxVertCount(const struct NVGpath* paths, int npaths) 647 | { 648 | int i, count = 0; 649 | for (i = 0; i < npaths; i++) { 650 | count += paths[i].nfill; 651 | count += paths[i].nstroke; 652 | } 653 | return count; 654 | } 655 | 656 | static void glnvg__uploadPaths(const struct NVGpath* paths, int npaths) 657 | { 658 | const struct NVGpath* path; 659 | int i, n = 0; 660 | for (i = 0; i < npaths; i++) { 661 | path = &paths[i]; 662 | if (path->nfill > 0) { 663 | glBufferSubData(GL_ARRAY_BUFFER, n*sizeof(struct NVGvertex), path->nfill * sizeof(struct NVGvertex), &path->fill[0].x); 664 | n += path->nfill; 665 | } 666 | if (path->nstroke > 0) { 667 | glBufferSubData(GL_ARRAY_BUFFER, n*sizeof(struct NVGvertex), path->nstroke * sizeof(struct NVGvertex), &path->stroke[0].x); 668 | n += path->nstroke; 669 | } 670 | } 671 | } 672 | 673 | static void glnvg__renderFill(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe, 674 | const float* bounds, const struct NVGpath* paths, int npaths) 675 | { 676 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 677 | const struct NVGpath* path; 678 | int i, n, offset, maxCount; 679 | 680 | if (gl->shader.prog == 0) 681 | return; 682 | 683 | maxCount = glnvg__maxVertCount(paths, npaths); 684 | glBindVertexArray(gl->vertArr); 685 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 686 | glBufferData(GL_ARRAY_BUFFER, maxCount * sizeof(struct NVGvertex), NULL, GL_STREAM_DRAW); 687 | glnvg__uploadPaths(paths, npaths); 688 | 689 | if (npaths == 1 && paths[0].convex) { 690 | 691 | glEnable(GL_CULL_FACE); 692 | 693 | glEnableVertexAttribArray(0); 694 | glEnableVertexAttribArray(1); 695 | glnvg__setupPaint(gl, paint, scissor, fringe, fringe); 696 | 697 | glDisable(GL_CULL_FACE); 698 | n = 0; 699 | for (i = 0; i < npaths; i++) { 700 | path = &paths[i]; 701 | offset = n * sizeof(struct NVGvertex); 702 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 703 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(offset + 2*sizeof(float))); 704 | glDrawArrays(GL_TRIANGLE_FAN, 0, path->nfill); 705 | n += path->nfill + path->nstroke; 706 | } 707 | 708 | glEnable(GL_CULL_FACE); 709 | 710 | if (gl->edgeAntiAlias) { 711 | // Draw fringes 712 | n = 0; 713 | for (i = 0; i < npaths; i++) { 714 | path = &paths[i]; 715 | offset = (n + path->nfill) * sizeof(struct NVGvertex); 716 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 717 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(offset + 2*sizeof(float))); 718 | glDrawArrays(GL_TRIANGLE_STRIP, 0, path->nstroke); 719 | n += path->nfill + path->nstroke; 720 | } 721 | } 722 | 723 | glUseProgram(0); 724 | glDisableVertexAttribArray(0); 725 | glDisableVertexAttribArray(1); 726 | 727 | } else { 728 | 729 | glEnable(GL_CULL_FACE); 730 | 731 | glBindVertexArray(gl->vertArr); 732 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 733 | 734 | // Draw shapes 735 | glDisable(GL_BLEND); 736 | glEnable(GL_STENCIL_TEST); 737 | glStencilMask(0xff); 738 | glStencilFunc(GL_ALWAYS, 0, ~0); 739 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 740 | 741 | glUseProgram(gl->shader.prog); 742 | glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], NSVG_SHADER_SIMPLE); 743 | glUniform2f(gl->shader.loc[GLNVG_LOC_VIEWSIZE], gl->viewWidth, gl->viewHeight); 744 | glnvg__checkError("fill solid loc"); 745 | 746 | glEnableVertexAttribArray(0); 747 | 748 | glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); 749 | glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); 750 | glDisable(GL_CULL_FACE); 751 | n = 0; 752 | for (i = 0; i < npaths; i++) { 753 | path = &paths[i]; 754 | offset = n * sizeof(struct NVGvertex); 755 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 756 | glDrawArrays(GL_TRIANGLE_FAN, 0, path->nfill); 757 | n += path->nfill + path->nstroke; 758 | } 759 | 760 | glEnable(GL_CULL_FACE); 761 | 762 | // Draw aliased off-pixels 763 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 764 | glEnable(GL_BLEND); 765 | 766 | glEnableVertexAttribArray(1); 767 | glnvg__setupPaint(gl, paint, scissor, fringe, fringe); 768 | 769 | if (gl->edgeAntiAlias) { 770 | glStencilFunc(GL_EQUAL, 0x00, 0xff); 771 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 772 | 773 | // Draw fringes 774 | n = 0; 775 | for (i = 0; i < npaths; i++) { 776 | path = &paths[i]; 777 | offset = (n + path->nfill) * sizeof(struct NVGvertex); 778 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 779 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(offset + 2*sizeof(float))); 780 | glDrawArrays(GL_TRIANGLE_STRIP, 0, path->nstroke); 781 | n += path->nfill + path->nstroke; 782 | } 783 | } 784 | 785 | // Draw fill 786 | glStencilFunc(GL_NOTEQUAL, 0x0, 0xff); 787 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); 788 | 789 | glDisableVertexAttribArray(1); 790 | 791 | float quad[6*2] = { 792 | bounds[0], bounds[3], bounds[2], bounds[3], bounds[2], bounds[1], 793 | bounds[0], bounds[3], bounds[2], bounds[1], bounds[0], bounds[1], 794 | }; 795 | glBufferSubData(GL_ARRAY_BUFFER, 0, 6 * 2*sizeof(float), quad); 796 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), (const GLvoid*)0); 797 | glVertexAttrib2f(1, 0.5f, 1.0f); 798 | glDrawArrays(GL_TRIANGLES, 0, 6); 799 | 800 | glUseProgram(0); 801 | 802 | glDisableVertexAttribArray(0); 803 | 804 | glDisable(GL_STENCIL_TEST); 805 | } 806 | } 807 | 808 | static void glnvg__renderStroke(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe, 809 | float width, const struct NVGpath* paths, int npaths) 810 | { 811 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 812 | const struct NVGpath* path; 813 | int i, n, offset, maxCount; 814 | 815 | if (gl->shader.prog == 0) 816 | return; 817 | 818 | glnvg__setupPaint(gl, paint, scissor, width, fringe); 819 | 820 | glEnable(GL_CULL_FACE); 821 | 822 | maxCount = glnvg__maxVertCount(paths, npaths); 823 | glBindVertexArray(gl->vertArr); 824 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 825 | glBufferData(GL_ARRAY_BUFFER, maxCount * sizeof(struct NVGvertex), NULL, GL_STREAM_DRAW); 826 | glnvg__uploadPaths(paths, npaths); 827 | 828 | glEnableVertexAttribArray(0); 829 | glEnableVertexAttribArray(1); 830 | 831 | // Draw Strokes 832 | n = 0; 833 | for (i = 0; i < npaths; i++) { 834 | path = &paths[i]; 835 | offset = (n + path->nfill) * sizeof(struct NVGvertex); 836 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)offset); 837 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(offset + 2*sizeof(float))); 838 | glDrawArrays(GL_TRIANGLE_STRIP, 0, path->nstroke); 839 | n += path->nfill + path->nstroke; 840 | } 841 | 842 | glDisableVertexAttribArray(0); 843 | glDisableVertexAttribArray(1); 844 | 845 | glUseProgram(0); 846 | } 847 | 848 | static void glnvg__renderTriangles(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, 849 | const struct NVGvertex* verts, int nverts) 850 | { 851 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 852 | struct GLNVGtexture* tex = glnvg__findTexture(gl, paint->image); 853 | struct NVGcolor color; 854 | NVG_NOTUSED(scissor); 855 | 856 | if (gl->shader.prog == 0) 857 | return; 858 | 859 | if (tex != NULL) { 860 | glBindTexture(GL_TEXTURE_2D, tex->tex); 861 | } 862 | 863 | glUseProgram(gl->shader.prog); 864 | glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], NSVG_SHADER_IMG); 865 | glUniform2f(gl->shader.loc[GLNVG_LOC_VIEWSIZE], gl->viewWidth, gl->viewHeight); 866 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); 867 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEXTYPE], tex->type == NVG_TEXTURE_RGBA ? 0 : 1); 868 | glnvg__checkError("tris solid img loc"); 869 | 870 | glBindVertexArray(gl->vertArr); 871 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 872 | glBufferData(GL_ARRAY_BUFFER, nverts * sizeof(struct NVGvertex), verts, GL_STREAM_DRAW); 873 | 874 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)0); 875 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(2 * sizeof(float))); 876 | glEnableVertexAttribArray(0); 877 | glEnableVertexAttribArray(1); 878 | 879 | color = paint->innerColor; 880 | glVertexAttrib4fv(2, color.rgba); 881 | 882 | glDrawArrays(GL_TRIANGLES, 0, nverts); 883 | 884 | glDisableVertexAttribArray(0); 885 | glDisableVertexAttribArray(1); 886 | } 887 | 888 | static void glnvg__renderDelete(void* uptr) 889 | { 890 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 891 | int i; 892 | if (gl == NULL) return; 893 | 894 | glnvg__deleteShader(&gl->shader); 895 | 896 | for (i = 0; i < gl->ntextures; i++) { 897 | if (gl->textures[i].tex != 0) 898 | glDeleteTextures(1, &gl->textures[i].tex); 899 | } 900 | free(gl->textures); 901 | 902 | free(gl); 903 | } 904 | 905 | 906 | #ifdef NANOVG_GLES3 907 | struct NVGcontext* nvgCreateGLES3(int atlasw, int atlash, int edgeaa) 908 | #else 909 | struct NVGcontext* nvgCreateGL3(int atlasw, int atlash, int edgeaa) 910 | #endif 911 | { 912 | struct NVGparams params; 913 | struct NVGcontext* ctx = NULL; 914 | struct GLNVGcontext* gl = (struct GLNVGcontext*)malloc(sizeof(struct GLNVGcontext)); 915 | if (gl == NULL) goto error; 916 | memset(gl, 0, sizeof(struct GLNVGcontext)); 917 | 918 | memset(¶ms, 0, sizeof(params)); 919 | params.renderCreate = glnvg__renderCreate; 920 | params.renderCreateTexture = glnvg__renderCreateTexture; 921 | params.renderDeleteTexture = glnvg__renderDeleteTexture; 922 | params.renderUpdateTexture = glnvg__renderUpdateTexture; 923 | params.renderGetTextureSize = glnvg__renderGetTextureSize; 924 | params.renderViewport = glnvg__renderViewport; 925 | params.renderFlush = glnvg__renderFlush; 926 | params.renderFill = glnvg__renderFill; 927 | params.renderStroke = glnvg__renderStroke; 928 | params.renderTriangles = glnvg__renderTriangles; 929 | params.renderDelete = glnvg__renderDelete; 930 | params.userPtr = gl; 931 | params.atlasWidth = atlasw; 932 | params.atlasHeight = atlash; 933 | params.edgeAntiAlias = edgeaa; 934 | 935 | gl->edgeAntiAlias = edgeaa; 936 | 937 | ctx = nvgCreateInternal(¶ms); 938 | if (ctx == NULL) goto error; 939 | 940 | return ctx; 941 | 942 | error: 943 | // 'gl' is freed by nvgDeleteInternal. 944 | if (ctx != NULL) nvgDeleteInternal(ctx); 945 | return NULL; 946 | } 947 | 948 | #ifdef NANOVG_GLES3 949 | void nvgDeleteGLES3(struct NVGcontext* ctx) 950 | #else 951 | void nvgDeleteGL3(struct NVGcontext* ctx) 952 | #endif 953 | { 954 | nvgDeleteInternal(ctx); 955 | } 956 | 957 | #endif 958 | -------------------------------------------------------------------------------- /obsolete/obsolete.md: -------------------------------------------------------------------------------- 1 | The files in this folder will be removed in near future. 2 | 3 | - nanovg_gl2.h and nanovg_gl3.h 4 | - These were the first GL2 and GL3 backends 5 | - an optimized version of the gl3 backed was build and later GL2 support was added to it 6 | - the new combined backend has superseded the individual backends -------------------------------------------------------------------------------- /premake4.lua: -------------------------------------------------------------------------------- 1 | 2 | local action = _ACTION or "" 3 | 4 | solution "nanovg" 5 | location ( "build" ) 6 | configurations { "Debug", "Release" } 7 | platforms {"native", "x64", "x32"} 8 | 9 | project "nanovg" 10 | language "C" 11 | kind "StaticLib" 12 | includedirs { "src" } 13 | files { "src/*.c" } 14 | targetdir("build") 15 | defines { "_CRT_SECURE_NO_WARNINGS" } --,"FONS_USE_FREETYPE" } Uncomment to compile with FreeType support 16 | 17 | configuration "Debug" 18 | defines { "DEBUG" } 19 | flags { "Symbols", "ExtraWarnings"} 20 | 21 | configuration "Release" 22 | defines { "NDEBUG" } 23 | flags { "Optimize", "ExtraWarnings"} 24 | 25 | project "example_gl2" 26 | 27 | kind "ConsoleApp" 28 | language "C" 29 | files { "example/example_gl2.c", "example/demo.c", "example/perf.c" } 30 | includedirs { "src", "example" } 31 | targetdir("build") 32 | links { "nanovg" } 33 | 34 | configuration { "linux" } 35 | linkoptions { "`pkg-config --libs glfw3`" } 36 | links { "GL", "GLU", "m", "GLEW" } 37 | defines { "NANOVG_GLEW" } 38 | 39 | configuration { "windows" } 40 | links { "glfw3", "gdi32", "winmm", "user32", "GLEW", "glu32","opengl32", "kernel32" } 41 | defines { "NANOVG_GLEW", "_CRT_SECURE_NO_WARNINGS" } 42 | 43 | configuration { "macosx" } 44 | links { "glfw3" } 45 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo", "-framework Carbon" } 46 | 47 | configuration "Debug" 48 | defines { "DEBUG" } 49 | flags { "Symbols", "ExtraWarnings"} 50 | 51 | configuration "Release" 52 | defines { "NDEBUG" } 53 | flags { "Optimize", "ExtraWarnings"} 54 | 55 | project "example_gl3" 56 | kind "ConsoleApp" 57 | language "C" 58 | files { "example/example_gl3.c", "example/demo.c", "example/perf.c" } 59 | includedirs { "src", "example" } 60 | targetdir("build") 61 | links { "nanovg" } 62 | 63 | configuration { "linux" } 64 | linkoptions { "`pkg-config --libs glfw3`" } 65 | links { "GL", "GLU", "m", "GLEW" } 66 | defines { "NANOVG_GLEW" } 67 | 68 | configuration { "windows" } 69 | links { "glfw3", "gdi32", "winmm", "user32", "GLEW", "glu32","opengl32", "kernel32" } 70 | defines { "NANOVG_GLEW", "_CRT_SECURE_NO_WARNINGS" } 71 | 72 | configuration { "macosx" } 73 | links { "glfw3" } 74 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo", "-framework Carbon" } 75 | 76 | configuration "Debug" 77 | defines { "DEBUG" } 78 | flags { "Symbols", "ExtraWarnings"} 79 | 80 | configuration "Release" 81 | defines { "NDEBUG" } 82 | flags { "Optimize", "ExtraWarnings"} 83 | 84 | project "example_gl2_msaa" 85 | kind "ConsoleApp" 86 | language "C" 87 | defines { "DEMO_MSAA" } 88 | files { "example/example_gl2.c", "example/demo.c", "example/perf.c" } 89 | includedirs { "src", "example" } 90 | targetdir("build") 91 | links { "nanovg" } 92 | 93 | configuration { "linux" } 94 | linkoptions { "`pkg-config --libs glfw3`" } 95 | links { "GL", "GLU", "m", "GLEW" } 96 | defines { "NANOVG_GLEW" } 97 | 98 | configuration { "windows" } 99 | links { "glfw3", "gdi32", "winmm", "user32", "GLEW", "glu32","opengl32", "kernel32" } 100 | defines { "NANOVG_GLEW", "_CRT_SECURE_NO_WARNINGS" } 101 | 102 | configuration { "macosx" } 103 | links { "glfw3" } 104 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo", "-framework Carbon" } 105 | 106 | configuration "Debug" 107 | defines { "DEBUG" } 108 | flags { "Symbols", "ExtraWarnings"} 109 | 110 | configuration "Release" 111 | defines { "NDEBUG" } 112 | flags { "Optimize", "ExtraWarnings"} 113 | 114 | project "example_gl3_msaa" 115 | kind "ConsoleApp" 116 | language "C" 117 | defines { "DEMO_MSAA" } 118 | files { "example/example_gl3.c", "example/demo.c", "example/perf.c" } 119 | includedirs { "src", "example" } 120 | targetdir("build") 121 | links { "nanovg" } 122 | 123 | configuration { "linux" } 124 | linkoptions { "`pkg-config --libs glfw3`" } 125 | links { "GL", "GLU", "m", "GLEW" } 126 | defines { "NANOVG_GLEW" } 127 | 128 | configuration { "windows" } 129 | links { "glfw3", "gdi32", "winmm", "user32", "GLEW", "glu32","opengl32", "kernel32" } 130 | defines { "NANOVG_GLEW", "_CRT_SECURE_NO_WARNINGS" } 131 | 132 | configuration { "macosx" } 133 | links { "glfw3" } 134 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo", "-framework Carbon" } 135 | 136 | configuration "Debug" 137 | defines { "DEBUG" } 138 | flags { "Symbols", "ExtraWarnings"} 139 | 140 | configuration "Release" 141 | defines { "NDEBUG" } 142 | flags { "Optimize", "ExtraWarnings"} 143 | 144 | project "example_fbo" 145 | kind "ConsoleApp" 146 | language "C" 147 | files { "example/example_fbo.c", "example/perf.c" } 148 | includedirs { "src", "example" } 149 | targetdir("build") 150 | links { "nanovg" } 151 | 152 | configuration { "linux" } 153 | linkoptions { "`pkg-config --libs glfw3`" } 154 | links { "GL", "GLU", "m", "GLEW" } 155 | 156 | configuration { "windows" } 157 | links { "glfw3", "gdi32", "winmm", "user32", "GLEW", "glu32","opengl32", "kernel32" } 158 | defines { "NANOVG_GLEW", "_CRT_SECURE_NO_WARNINGS" } 159 | 160 | configuration { "macosx" } 161 | links { "glfw3" } 162 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo", "-framework Carbon" } 163 | 164 | configuration "Debug" 165 | defines { "DEBUG" } 166 | flags { "Symbols", "ExtraWarnings"} 167 | 168 | configuration "Release" 169 | defines { "NDEBUG" } 170 | flags { "Optimize", "ExtraWarnings"} 171 | 172 | project "example_gles2" 173 | kind "ConsoleApp" 174 | language "C" 175 | files { "example/example_gles2.c", "example/demo.c", "example/perf.c" } 176 | includedirs { "src", "example" } 177 | targetdir("build") 178 | links { "nanovg" } 179 | 180 | configuration { "linux" } 181 | linkoptions { "`pkg-config --libs glfw3`" } 182 | links { "GL", "GLU", "m", "GLEW" } 183 | 184 | configuration { "windows" } 185 | links { "glfw3", "gdi32", "winmm", "user32", "GLEW", "glu32","opengl32", "kernel32" } 186 | defines { "NANOVG_GLEW", "_CRT_SECURE_NO_WARNINGS" } 187 | 188 | configuration { "macosx" } 189 | links { "glfw3" } 190 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo", "-framework Carbon" } 191 | 192 | configuration "Debug" 193 | defines { "DEBUG" } 194 | flags { "Symbols", "ExtraWarnings"} 195 | 196 | configuration "Release" 197 | defines { "NDEBUG" } 198 | flags { "Optimize", "ExtraWarnings"} 199 | 200 | project "example_gles3" 201 | kind "ConsoleApp" 202 | language "C" 203 | files { "example/example_gles3.c", "example/demo.c", "example/perf.c" } 204 | includedirs { "src", "example" } 205 | targetdir("build") 206 | links { "nanovg" } 207 | 208 | configuration { "linux" } 209 | linkoptions { "`pkg-config --libs glfw3`" } 210 | links { "GL", "GLU", "m", "GLEW" } 211 | 212 | configuration { "windows" } 213 | links { "glfw3", "gdi32", "winmm", "user32", "GLEW", "glu32","opengl32", "kernel32" } 214 | defines { "NANOVG_GLEW", "_CRT_SECURE_NO_WARNINGS" } 215 | 216 | configuration { "macosx" } 217 | links { "glfw3" } 218 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo", "-framework Carbon" } 219 | 220 | configuration "Debug" 221 | defines { "DEBUG" } 222 | flags { "Symbols", "ExtraWarnings"} 223 | 224 | configuration "Release" 225 | defines { "NDEBUG" } 226 | flags { "Optimize", "ExtraWarnings"} 227 | -------------------------------------------------------------------------------- /src/nanovg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #ifndef NANOVG_H 20 | #define NANOVG_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define NVG_PI 3.14159265358979323846264338327f 27 | 28 | #ifdef _MSC_VER 29 | #pragma warning(push) 30 | #pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union 31 | #endif 32 | 33 | typedef struct NVGcontext NVGcontext; 34 | 35 | struct NVGcolor { 36 | union { 37 | float rgba[4]; 38 | struct { 39 | float r,g,b,a; 40 | }; 41 | }; 42 | }; 43 | typedef struct NVGcolor NVGcolor; 44 | 45 | struct NVGpaint { 46 | float xform[6]; 47 | float extent[2]; 48 | float radius; 49 | float feather; 50 | NVGcolor innerColor; 51 | NVGcolor outerColor; 52 | int image; 53 | }; 54 | typedef struct NVGpaint NVGpaint; 55 | 56 | enum NVGwinding { 57 | NVG_CCW = 1, // Winding for solid shapes 58 | NVG_CW = 2, // Winding for holes 59 | }; 60 | 61 | enum NVGsolidity { 62 | NVG_SOLID = 1, // CCW 63 | NVG_HOLE = 2, // CW 64 | }; 65 | 66 | enum NVGlineCap { 67 | NVG_BUTT, 68 | NVG_ROUND, 69 | NVG_SQUARE, 70 | NVG_BEVEL, 71 | NVG_MITER, 72 | }; 73 | 74 | enum NVGalign { 75 | // Horizontal align 76 | NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left. 77 | NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center. 78 | NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right. 79 | // Vertical align 80 | NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. 81 | NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. 82 | NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. 83 | NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. 84 | }; 85 | 86 | enum NVGblendFactor { 87 | NVG_ZERO = 1<<0, 88 | NVG_ONE = 1<<1, 89 | NVG_SRC_COLOR = 1<<2, 90 | NVG_ONE_MINUS_SRC_COLOR = 1<<3, 91 | NVG_DST_COLOR = 1<<4, 92 | NVG_ONE_MINUS_DST_COLOR = 1<<5, 93 | NVG_SRC_ALPHA = 1<<6, 94 | NVG_ONE_MINUS_SRC_ALPHA = 1<<7, 95 | NVG_DST_ALPHA = 1<<8, 96 | NVG_ONE_MINUS_DST_ALPHA = 1<<9, 97 | NVG_SRC_ALPHA_SATURATE = 1<<10, 98 | }; 99 | 100 | enum NVGcompositeOperation { 101 | NVG_SOURCE_OVER, 102 | NVG_SOURCE_IN, 103 | NVG_SOURCE_OUT, 104 | NVG_ATOP, 105 | NVG_DESTINATION_OVER, 106 | NVG_DESTINATION_IN, 107 | NVG_DESTINATION_OUT, 108 | NVG_DESTINATION_ATOP, 109 | NVG_LIGHTER, 110 | NVG_COPY, 111 | NVG_XOR, 112 | }; 113 | 114 | struct NVGcompositeOperationState { 115 | int srcRGB; 116 | int dstRGB; 117 | int srcAlpha; 118 | int dstAlpha; 119 | }; 120 | typedef struct NVGcompositeOperationState NVGcompositeOperationState; 121 | 122 | struct NVGglyphPosition { 123 | const char* str; // Position of the glyph in the input string. 124 | float x; // The x-coordinate of the logical glyph position. 125 | float minx, maxx; // The bounds of the glyph shape. 126 | }; 127 | typedef struct NVGglyphPosition NVGglyphPosition; 128 | 129 | struct NVGtextRow { 130 | const char* start; // Pointer to the input text where the row starts. 131 | const char* end; // Pointer to the input text where the row ends (one past the last character). 132 | const char* next; // Pointer to the beginning of the next row. 133 | float width; // Logical width of the row. 134 | float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. 135 | }; 136 | typedef struct NVGtextRow NVGtextRow; 137 | 138 | enum NVGimageFlags { 139 | NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. 140 | NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. 141 | NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. 142 | NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. 143 | NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. 144 | NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear 145 | }; 146 | 147 | // Begin drawing a new frame 148 | // Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() 149 | // nvgBeginFrame() defines the size of the window to render to in relation currently 150 | // set viewport (i.e. glViewport on GL backends). Device pixel ration allows to 151 | // control the rendering on Hi-DPI devices. 152 | // For example, GLFW returns two dimension for an opened window: window size and 153 | // frame buffer size. In that case you would set windowWidth/Height to the window size 154 | // devicePixelRatio to: frameBufferWidth / windowWidth. 155 | void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio); 156 | 157 | // Cancels drawing the current frame. 158 | void nvgCancelFrame(NVGcontext* ctx); 159 | 160 | // Ends drawing flushing remaining render state. 161 | void nvgEndFrame(NVGcontext* ctx); 162 | 163 | // 164 | // Composite operation 165 | // 166 | // The composite operations in NanoVG are modeled after HTML Canvas API, and 167 | // the blend func is based on OpenGL (see corresponding manuals for more info). 168 | // The colors in the blending state have premultiplied alpha. 169 | 170 | // Sets the composite operation. The op parameter should be one of NVGcompositeOperation. 171 | void nvgGlobalCompositeOperation(NVGcontext* ctx, int op); 172 | 173 | // Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor. 174 | void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor); 175 | 176 | // Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor. 177 | void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha); 178 | 179 | // 180 | // Color utils 181 | // 182 | // Colors in NanoVG are stored as unsigned ints in ABGR format. 183 | 184 | // Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f). 185 | NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b); 186 | 187 | // Returns a color value from red, green, blue values. Alpha will be set to 1.0f. 188 | NVGcolor nvgRGBf(float r, float g, float b); 189 | 190 | 191 | // Returns a color value from red, green, blue and alpha values. 192 | NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); 193 | 194 | // Returns a color value from red, green, blue and alpha values. 195 | NVGcolor nvgRGBAf(float r, float g, float b, float a); 196 | 197 | 198 | // Linearly interpolates from color c0 to c1, and returns resulting color value. 199 | NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u); 200 | 201 | // Sets transparency of a color value. 202 | NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a); 203 | 204 | // Sets transparency of a color value. 205 | NVGcolor nvgTransRGBAf(NVGcolor c0, float a); 206 | 207 | // Returns color value specified by hue, saturation and lightness. 208 | // HSL values are all in range [0..1], alpha will be set to 255. 209 | NVGcolor nvgHSL(float h, float s, float l); 210 | 211 | // Returns color value specified by hue, saturation and lightness and alpha. 212 | // HSL values are all in range [0..1], alpha in range [0..255] 213 | NVGcolor nvgHSLA(float h, float s, float l, unsigned char a); 214 | 215 | // 216 | // State Handling 217 | // 218 | // NanoVG contains state which represents how paths will be rendered. 219 | // The state contains transform, fill and stroke styles, text and font styles, 220 | // and scissor clipping. 221 | 222 | // Pushes and saves the current render state into a state stack. 223 | // A matching nvgRestore() must be used to restore the state. 224 | void nvgSave(NVGcontext* ctx); 225 | 226 | // Pops and restores current render state. 227 | void nvgRestore(NVGcontext* ctx); 228 | 229 | // Resets current render state to default values. Does not affect the render state stack. 230 | void nvgReset(NVGcontext* ctx); 231 | 232 | // 233 | // Render styles 234 | // 235 | // Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern. 236 | // Solid color is simply defined as a color value, different kinds of paints can be created 237 | // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). 238 | // 239 | // Current render style can be saved and restored using nvgSave() and nvgRestore(). 240 | 241 | // Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default. 242 | void nvgShapeAntiAlias(NVGcontext* ctx, int enabled); 243 | 244 | // Sets current stroke style to a solid color. 245 | void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); 246 | 247 | // Sets current stroke style to a paint, which can be a one of the gradients or a pattern. 248 | void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint); 249 | 250 | // Sets current fill style to a solid color. 251 | void nvgFillColor(NVGcontext* ctx, NVGcolor color); 252 | 253 | // Sets current fill style to a paint, which can be a one of the gradients or a pattern. 254 | void nvgFillPaint(NVGcontext* ctx, NVGpaint paint); 255 | 256 | // Sets the miter limit of the stroke style. 257 | // Miter limit controls when a sharp corner is beveled. 258 | void nvgMiterLimit(NVGcontext* ctx, float limit); 259 | 260 | // Sets the stroke width of the stroke style. 261 | void nvgStrokeWidth(NVGcontext* ctx, float size); 262 | 263 | // Sets how the end of the line (cap) is drawn, 264 | // Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. 265 | void nvgLineCap(NVGcontext* ctx, int cap); 266 | 267 | // Sets how sharp path corners are drawn. 268 | // Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL. 269 | void nvgLineJoin(NVGcontext* ctx, int join); 270 | 271 | // Sets the transparency applied to all rendered shapes. 272 | // Already transparent paths will get proportionally more transparent as well. 273 | void nvgGlobalAlpha(NVGcontext* ctx, float alpha); 274 | 275 | // 276 | // Transforms 277 | // 278 | // The paths, gradients, patterns and scissor region are transformed by an transformation 279 | // matrix at the time when they are passed to the API. 280 | // The current transformation matrix is a affine matrix: 281 | // [sx kx tx] 282 | // [ky sy ty] 283 | // [ 0 0 1] 284 | // Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. 285 | // The last row is assumed to be 0,0,1 and is not stored. 286 | // 287 | // Apart from nvgResetTransform(), each transformation function first creates 288 | // specific transformation matrix and pre-multiplies the current transformation by it. 289 | // 290 | // Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). 291 | 292 | // Resets current transform to a identity matrix. 293 | void nvgResetTransform(NVGcontext* ctx); 294 | 295 | // Premultiplies current coordinate system by specified matrix. 296 | // The parameters are interpreted as matrix as follows: 297 | // [a c e] 298 | // [b d f] 299 | // [0 0 1] 300 | void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f); 301 | 302 | // Translates current coordinate system. 303 | void nvgTranslate(NVGcontext* ctx, float x, float y); 304 | 305 | // Rotates current coordinate system. Angle is specified in radians. 306 | void nvgRotate(NVGcontext* ctx, float angle); 307 | 308 | // Skews the current coordinate system along X axis. Angle is specified in radians. 309 | void nvgSkewX(NVGcontext* ctx, float angle); 310 | 311 | // Skews the current coordinate system along Y axis. Angle is specified in radians. 312 | void nvgSkewY(NVGcontext* ctx, float angle); 313 | 314 | // Scales the current coordinate system. 315 | void nvgScale(NVGcontext* ctx, float x, float y); 316 | 317 | // Stores the top part (a-f) of the current transformation matrix in to the specified buffer. 318 | // [a c e] 319 | // [b d f] 320 | // [0 0 1] 321 | // There should be space for 6 floats in the return buffer for the values a-f. 322 | void nvgCurrentTransform(NVGcontext* ctx, float* xform); 323 | 324 | 325 | // The following functions can be used to make calculations on 2x3 transformation matrices. 326 | // A 2x3 matrix is represented as float[6]. 327 | 328 | // Sets the transform to identity matrix. 329 | void nvgTransformIdentity(float* dst); 330 | 331 | // Sets the transform to translation matrix matrix. 332 | void nvgTransformTranslate(float* dst, float tx, float ty); 333 | 334 | // Sets the transform to scale matrix. 335 | void nvgTransformScale(float* dst, float sx, float sy); 336 | 337 | // Sets the transform to rotate matrix. Angle is specified in radians. 338 | void nvgTransformRotate(float* dst, float a); 339 | 340 | // Sets the transform to skew-x matrix. Angle is specified in radians. 341 | void nvgTransformSkewX(float* dst, float a); 342 | 343 | // Sets the transform to skew-y matrix. Angle is specified in radians. 344 | void nvgTransformSkewY(float* dst, float a); 345 | 346 | // Sets the transform to the result of multiplication of two transforms, of A = A*B. 347 | void nvgTransformMultiply(float* dst, const float* src); 348 | 349 | // Sets the transform to the result of multiplication of two transforms, of A = B*A. 350 | void nvgTransformPremultiply(float* dst, const float* src); 351 | 352 | // Sets the destination to inverse of specified transform. 353 | // Returns 1 if the inverse could be calculated, else 0. 354 | int nvgTransformInverse(float* dst, const float* src); 355 | 356 | // Transform a point by given transform. 357 | void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy); 358 | 359 | // Converts degrees to radians and vice versa. 360 | float nvgDegToRad(float deg); 361 | float nvgRadToDeg(float rad); 362 | 363 | // 364 | // Images 365 | // 366 | // NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. 367 | // In addition you can upload your own image. The image loading is provided by stb_image. 368 | // The parameter imageFlags is combination of flags defined in NVGimageFlags. 369 | 370 | // Creates image by loading it from the disk from specified file name. 371 | // Returns handle to the image. 372 | int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); 373 | 374 | // Creates image by loading it from the specified chunk of memory. 375 | // Returns handle to the image. 376 | int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); 377 | 378 | // Creates image from specified image data. 379 | // Returns handle to the image. 380 | int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data); 381 | 382 | // Updates image data specified by image handle. 383 | void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data); 384 | 385 | // Returns the dimensions of a created image. 386 | void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h); 387 | 388 | // Deletes created image. 389 | void nvgDeleteImage(NVGcontext* ctx, int image); 390 | 391 | // 392 | // Paints 393 | // 394 | // NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. 395 | // These can be used as paints for strokes and fills. 396 | 397 | // Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates 398 | // of the linear gradient, icol specifies the start color and ocol the end color. 399 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 400 | NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey, 401 | NVGcolor icol, NVGcolor ocol); 402 | 403 | // Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering 404 | // drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, 405 | // (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry 406 | // the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. 407 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 408 | NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, 409 | float r, float f, NVGcolor icol, NVGcolor ocol); 410 | 411 | // Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify 412 | // the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. 413 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 414 | NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, 415 | NVGcolor icol, NVGcolor ocol); 416 | 417 | // Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern, 418 | // (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. 419 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 420 | NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, 421 | float angle, int image, float alpha); 422 | 423 | // 424 | // Scissoring 425 | // 426 | // Scissoring allows you to clip the rendering into a rectangle. This is useful for various 427 | // user interface cases like rendering a text edit or a timeline. 428 | 429 | // Sets the current scissor rectangle. 430 | // The scissor rectangle is transformed by the current transform. 431 | void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h); 432 | 433 | // Intersects current scissor rectangle with the specified rectangle. 434 | // The scissor rectangle is transformed by the current transform. 435 | // Note: in case the rotation of previous scissor rect differs from 436 | // the current one, the intersection will be done between the specified 437 | // rectangle and the previous scissor rectangle transformed in the current 438 | // transform space. The resulting shape is always rectangle. 439 | void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h); 440 | 441 | // Reset and disables scissoring. 442 | void nvgResetScissor(NVGcontext* ctx); 443 | 444 | // 445 | // Paths 446 | // 447 | // Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths. 448 | // Then you define one or more paths and sub-paths which describe the shape. The are functions 449 | // to draw common shapes like rectangles and circles, and lower level step-by-step functions, 450 | // which allow to define a path curve by curve. 451 | // 452 | // NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise 453 | // winding and holes should have counter clockwise order. To specify winding of a path you can 454 | // call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW. 455 | // 456 | // Finally you can fill the path using current fill style by calling nvgFill(), and stroke it 457 | // with current stroke style by calling nvgStroke(). 458 | // 459 | // The curve segments and sub-paths are transformed by the current transform. 460 | 461 | // Clears the current path and sub-paths. 462 | void nvgBeginPath(NVGcontext* ctx); 463 | 464 | // Starts new sub-path with specified point as first point. 465 | void nvgMoveTo(NVGcontext* ctx, float x, float y); 466 | 467 | // Adds line segment from the last point in the path to the specified point. 468 | void nvgLineTo(NVGcontext* ctx, float x, float y); 469 | 470 | // Adds cubic bezier segment from last point in the path via two control points to the specified point. 471 | void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y); 472 | 473 | // Adds quadratic bezier segment from last point in the path via a control point to the specified point. 474 | void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y); 475 | 476 | // Adds an arc segment at the corner defined by the last path point, and two specified points. 477 | void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius); 478 | 479 | // Closes current sub-path with a line segment. 480 | void nvgClosePath(NVGcontext* ctx); 481 | 482 | // Sets the current sub-path winding, see NVGwinding and NVGsolidity. 483 | void nvgPathWinding(NVGcontext* ctx, int dir); 484 | 485 | // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, 486 | // and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW). 487 | // Angles are specified in radians. 488 | void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir); 489 | 490 | // Creates new rectangle shaped sub-path. 491 | void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); 492 | 493 | // Creates new rounded rectangle shaped sub-path. 494 | void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); 495 | 496 | // Creates new rounded rectangle shaped sub-path with varying radii for each corner. 497 | void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft); 498 | 499 | // Creates new ellipse shaped sub-path. 500 | void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); 501 | 502 | // Creates new circle shaped sub-path. 503 | void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); 504 | 505 | // Fills the current path with current fill style. 506 | void nvgFill(NVGcontext* ctx); 507 | 508 | // Fills the current path with current stroke style. 509 | void nvgStroke(NVGcontext* ctx); 510 | 511 | 512 | // 513 | // Text 514 | // 515 | // NanoVG allows you to load .ttf files and use the font to render text. 516 | // 517 | // The appearance of the text can be defined by setting the current text style 518 | // and by specifying the fill color. Common text and font settings such as 519 | // font size, letter spacing and text align are supported. Font blur allows you 520 | // to create simple text effects such as drop shadows. 521 | // 522 | // At render time the font face can be set based on the font handles or name. 523 | // 524 | // Font measure functions return values in local space, the calculations are 525 | // carried in the same resolution as the final rendering. This is done because 526 | // the text glyph positions are snapped to the nearest pixels sharp rendering. 527 | // 528 | // The local space means that values are not rotated or scale as per the current 529 | // transformation. For example if you set font size to 12, which would mean that 530 | // line height is 16, then regardless of the current scaling and rotation, the 531 | // returned line height is always 16. Some measures may vary because of the scaling 532 | // since aforementioned pixel snapping. 533 | // 534 | // While this may sound a little odd, the setup allows you to always render the 535 | // same way regardless of scaling. I.e. following works regardless of scaling: 536 | // 537 | // const char* txt = "Text me up."; 538 | // nvgTextBounds(vg, x,y, txt, NULL, bounds); 539 | // nvgBeginPath(vg); 540 | // nvgRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); 541 | // nvgFill(vg); 542 | // 543 | // Note: currently only solid color fill is supported for text. 544 | 545 | // Creates font by loading it from the disk from specified file name. 546 | // Returns handle to the font. 547 | int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); 548 | 549 | // fontIndex specifies which font face to load from a .ttf/.ttc file. 550 | int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex); 551 | 552 | // Creates font by loading it from the specified memory chunk. 553 | // Returns handle to the font. 554 | int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); 555 | 556 | // fontIndex specifies which font face to load from a .ttf/.ttc file. 557 | int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex); 558 | 559 | // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. 560 | int nvgFindFont(NVGcontext* ctx, const char* name); 561 | 562 | // Adds a fallback font by handle. 563 | int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont); 564 | 565 | // Adds a fallback font by name. 566 | int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont); 567 | 568 | // Resets fallback fonts by handle. 569 | void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont); 570 | 571 | // Resets fallback fonts by name. 572 | void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont); 573 | 574 | // Sets the font size of current text style. 575 | void nvgFontSize(NVGcontext* ctx, float size); 576 | 577 | // Sets the blur of current text style. 578 | void nvgFontBlur(NVGcontext* ctx, float blur); 579 | 580 | // Sets the letter spacing of current text style. 581 | void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); 582 | 583 | // Sets the proportional line height of current text style. The line height is specified as multiple of font size. 584 | void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); 585 | 586 | // Sets the text align of current text style, see NVGalign for options. 587 | void nvgTextAlign(NVGcontext* ctx, int align); 588 | 589 | // Sets the font face based on specified id of current text style. 590 | void nvgFontFaceId(NVGcontext* ctx, int font); 591 | 592 | // Sets the font face based on specified name of current text style. 593 | void nvgFontFace(NVGcontext* ctx, const char* font); 594 | 595 | // Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. 596 | float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end); 597 | 598 | // 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. 599 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 600 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 601 | void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end); 602 | 603 | // Measures the specified text string. Parameter bounds should be a pointer to float[4], 604 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 605 | // Returns the horizontal advance of the measured text (i.e. where the next character should drawn). 606 | // Measured values are returned in local coordinate space. 607 | float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds); 608 | 609 | // Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], 610 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 611 | // Measured values are returned in local coordinate space. 612 | void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds); 613 | 614 | // Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. 615 | // Measured values are returned in local coordinate space. 616 | int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions); 617 | 618 | // Returns the vertical metrics based on the current text style. 619 | // Measured values are returned in local coordinate space. 620 | void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh); 621 | 622 | // Breaks the specified text into lines. If end is specified only the sub-string will be used. 623 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 624 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 625 | int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows); 626 | 627 | // 628 | // Internal Render API 629 | // 630 | enum NVGtexture { 631 | NVG_TEXTURE_ALPHA = 0x01, 632 | NVG_TEXTURE_RGBA = 0x02, 633 | }; 634 | 635 | struct NVGscissor { 636 | float xform[6]; 637 | float extent[2]; 638 | }; 639 | typedef struct NVGscissor NVGscissor; 640 | 641 | struct NVGvertex { 642 | float x,y,u,v; 643 | }; 644 | typedef struct NVGvertex NVGvertex; 645 | 646 | struct NVGpath { 647 | int first; 648 | int count; 649 | unsigned char closed; 650 | int nbevel; 651 | NVGvertex* fill; 652 | int nfill; 653 | NVGvertex* stroke; 654 | int nstroke; 655 | int winding; 656 | int convex; 657 | }; 658 | typedef struct NVGpath NVGpath; 659 | 660 | struct NVGparams { 661 | void* userPtr; 662 | int edgeAntiAlias; 663 | int (*renderCreate)(void* uptr); 664 | int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); 665 | int (*renderDeleteTexture)(void* uptr, int image); 666 | int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); 667 | int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); 668 | void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio); 669 | void (*renderCancel)(void* uptr); 670 | void (*renderFlush)(void* uptr); 671 | void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); 672 | void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); 673 | void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe); 674 | void (*renderDelete)(void* uptr); 675 | }; 676 | typedef struct NVGparams NVGparams; 677 | 678 | // Constructor and destructor, called by the render back-end. 679 | NVGcontext* nvgCreateInternal(NVGparams* params); 680 | void nvgDeleteInternal(NVGcontext* ctx); 681 | 682 | NVGparams* nvgInternalParams(NVGcontext* ctx); 683 | 684 | // Debug function to dump cached path data. 685 | void nvgDebugDumpPathCache(NVGcontext* ctx); 686 | 687 | #ifdef _MSC_VER 688 | #pragma warning(pop) 689 | #endif 690 | 691 | #define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; } 692 | 693 | #ifdef __cplusplus 694 | } 695 | #endif 696 | 697 | #endif // NANOVG_H 698 | -------------------------------------------------------------------------------- /src/nanovg_gl_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | #ifndef NANOVG_GL_UTILS_H 19 | #define NANOVG_GL_UTILS_H 20 | 21 | struct NVGLUframebuffer { 22 | NVGcontext* ctx; 23 | GLuint fbo; 24 | GLuint rbo; 25 | GLuint texture; 26 | int image; 27 | }; 28 | typedef struct NVGLUframebuffer NVGLUframebuffer; 29 | 30 | // Helper function to create GL frame buffer to render to. 31 | void nvgluBindFramebuffer(NVGLUframebuffer* fb); 32 | NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); 33 | void nvgluDeleteFramebuffer(NVGLUframebuffer* fb); 34 | 35 | #endif // NANOVG_GL_UTILS_H 36 | 37 | #ifdef NANOVG_GL_IMPLEMENTATION 38 | 39 | #if defined(NANOVG_GL3) || defined(NANOVG_GLES2) || defined(NANOVG_GLES3) 40 | // FBO is core in OpenGL 3>. 41 | # define NANOVG_FBO_VALID 1 42 | #elif defined(NANOVG_GL2) 43 | // On OS X including glext defines FBO on GL2 too. 44 | # ifdef __APPLE__ 45 | # include 46 | # define NANOVG_FBO_VALID 1 47 | # endif 48 | #endif 49 | 50 | static GLint defaultFBO = -1; 51 | 52 | NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags) 53 | { 54 | #ifdef NANOVG_FBO_VALID 55 | GLint defaultFBO; 56 | GLint defaultRBO; 57 | NVGLUframebuffer* fb = NULL; 58 | 59 | glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); 60 | glGetIntegerv(GL_RENDERBUFFER_BINDING, &defaultRBO); 61 | 62 | fb = (NVGLUframebuffer*)malloc(sizeof(NVGLUframebuffer)); 63 | if (fb == NULL) goto error; 64 | memset(fb, 0, sizeof(NVGLUframebuffer)); 65 | 66 | fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); 67 | 68 | #if defined NANOVG_GL2 69 | fb->texture = nvglImageHandleGL2(ctx, fb->image); 70 | #elif defined NANOVG_GL3 71 | fb->texture = nvglImageHandleGL3(ctx, fb->image); 72 | #elif defined NANOVG_GLES2 73 | fb->texture = nvglImageHandleGLES2(ctx, fb->image); 74 | #elif defined NANOVG_GLES3 75 | fb->texture = nvglImageHandleGLES3(ctx, fb->image); 76 | #endif 77 | 78 | fb->ctx = ctx; 79 | 80 | // frame buffer object 81 | glGenFramebuffers(1, &fb->fbo); 82 | glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo); 83 | 84 | // render buffer object 85 | glGenRenderbuffers(1, &fb->rbo); 86 | glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo); 87 | glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h); 88 | 89 | // combine all 90 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); 91 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); 92 | 93 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 94 | #ifdef GL_DEPTH24_STENCIL8 95 | // If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback. 96 | // Some graphics cards require a depth buffer along with a stencil. 97 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); 98 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); 99 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); 100 | 101 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 102 | #endif // GL_DEPTH24_STENCIL8 103 | goto error; 104 | } 105 | 106 | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); 107 | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); 108 | return fb; 109 | error: 110 | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); 111 | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); 112 | nvgluDeleteFramebuffer(fb); 113 | return NULL; 114 | #else 115 | NVG_NOTUSED(ctx); 116 | NVG_NOTUSED(w); 117 | NVG_NOTUSED(h); 118 | NVG_NOTUSED(imageFlags); 119 | return NULL; 120 | #endif 121 | } 122 | 123 | void nvgluBindFramebuffer(NVGLUframebuffer* fb) 124 | { 125 | #ifdef NANOVG_FBO_VALID 126 | if (defaultFBO == -1) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); 127 | glBindFramebuffer(GL_FRAMEBUFFER, fb != NULL ? fb->fbo : defaultFBO); 128 | #else 129 | NVG_NOTUSED(fb); 130 | #endif 131 | } 132 | 133 | void nvgluDeleteFramebuffer(NVGLUframebuffer* fb) 134 | { 135 | #ifdef NANOVG_FBO_VALID 136 | if (fb == NULL) return; 137 | if (fb->fbo != 0) 138 | glDeleteFramebuffers(1, &fb->fbo); 139 | if (fb->rbo != 0) 140 | glDeleteRenderbuffers(1, &fb->rbo); 141 | if (fb->image >= 0) 142 | nvgDeleteImage(fb->ctx, fb->image); 143 | fb->ctx = NULL; 144 | fb->fbo = 0; 145 | fb->rbo = 0; 146 | fb->texture = 0; 147 | fb->image = -1; 148 | free(fb); 149 | #else 150 | NVG_NOTUSED(fb); 151 | #endif 152 | } 153 | 154 | #endif // NANOVG_GL_IMPLEMENTATION 155 | --------------------------------------------------------------------------------