Browse Source

First commit

shared-context
Mikko Mononen 8 years ago
commit
b89907ba6b
34 changed files with 11951 additions and 0 deletions
  1. +28
    -0
      .gitignore
  2. +138
    -0
      README.md
  3. BIN
      example/FiraSans-Bold.ttf
  4. BIN
      example/FiraSans-Light.ttf
  5. BIN
      example/FiraSans-Regular.ttf
  6. BIN
      example/Roboto-Bold.ttf
  7. BIN
      example/Roboto-Light.ttf
  8. BIN
      example/Roboto-Regular.ttf
  9. BIN
      example/entypo.ttf
  10. +767
    -0
      example/example.c
  11. +116
    -0
      example/glstash.h
  12. +431
    -0
      example/icons.txt
  13. +13
    -0
      example/images.txt
  14. BIN
      example/images/image1.jpg
  15. BIN
      example/images/image10.jpg
  16. BIN
      example/images/image11.jpg
  17. BIN
      example/images/image12.jpg
  18. BIN
      example/images/image2.jpg
  19. BIN
      example/images/image3.jpg
  20. BIN
      example/images/image4.jpg
  21. BIN
      example/images/image5.jpg
  22. BIN
      example/images/image6.jpg
  23. BIN
      example/images/image7.jpg
  24. BIN
      example/images/image8.jpg
  25. BIN
      example/images/image9.jpg
  26. BIN
      example/mfglabsiconset-webfont.ttf
  27. +84
    -0
      example/templates.xml
  28. +32
    -0
      premake4.lua
  29. +1134
    -0
      src/fontstash.h
  30. +690
    -0
      src/glnanovg.h
  31. +1586
    -0
      src/nanovg.c
  32. +194
    -0
      src/nanovg.h
  33. +4673
    -0
      src/stb_image.c
  34. +2065
    -0
      src/stb_truetype.h

+ 28
- 0
.gitignore View File

@@ -0,0 +1,28 @@
## Compiled source #
*.com
*.class
*.dll
*.exe
*.o
*.so
test

## Logs and databases #
*.log
*.sql
*.sqlite

## OS generated files #
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

## Build dir
build/*

## xcode specific
*xcuserdata*

+ 138
- 0
README.md View File

@@ -0,0 +1,138 @@
Font Stash
==========

Font stash is light-weight online font texture atlas builder written in C. It uses [stb_truetype](http://nothings.org) to render fonts on demand to a texture atlas.

The code is split in two parts, the font atlas and glyph quad generator [fontstash.h](/src/fontstash.h), and an example OpenGL backend ([glstash.h](/glstash.h).

## Screenshot

![screenshot of some text rendered witht the sample program](/screenshots/screen-01.png?raw=true)

## Example
``` C
// Create stash for 512x512 texture, our coordinate system has zero at top-left.
struct FONSparams params;
memset(&params, 0, sizeof(params));
params.width = 512;
params.height = 512;
params.flags = FONS_ZERO_TOPLEFT;
glstInit(&params);
struct FONScontext* fs = fonsCreate(&params);

// Add font to stash.
int fontNormal = fonsAddFont(fs, "DroidSerif-Regular.ttf");

// Render some text
float dx = 10, dy = 10;
unsigned int white = glstRGBA(255,255,255,255);
unsigned int brown = glstRGBA(192,128,0,128);

struct fontstash_style styleBig = { FONT_NORMAL, 124.0f, white };

fonsSetFont(fs, fontNormal);
fonsSetSize(fs, 124.0f);
fonsSetColor(fs, white);
fonsDrawText(fs, dx,dy,"The big ", &dx);

fonsSetSize(fs, 24.0f);
fonsSetColor(fs, brown);
fonsDrawText(fs, dx,dy,"brown fox", &dx);
```

## Using Font Stash in your project

In order to use fontstash in your own project, just copy fontstash.h, stb_truetype.h, and potentially glstash.h to your project.
In one C/C++ define FONTSTASH_IMPLEMENTATION before including the library to expand the font stash implementation in that file.

``` C
#include <stdio.h> // malloc, free, fopen, fclose, ftell, fseek, fread
#include <string.h> // memset
#define FONTSTASH_IMPLEMENTATION // Expands implementation
#include "fontstash.h"
```

``` C
#include <GLFW/glfw3.h> // Or any other GL header of your choice.
#define GLSTASH_IMPLEMENTATION // Expands implementation
#include "glstash.h"
```

## Creating new rendering backend

The default rendering backend uses OpenGL to render the glyphs. If you want to render the text using some other API, or want tighter integration with your code base you can write your own rendering backend. Take a look at the [glstash.h](/src/glstash.h) for reference implementation.

The rendering interface FontStash assumes access to is defined in the FONSparams structure. The call to `glstInit()` fills in variables.

```C
struct FONSparams {
...
void* userPtr;
int (*renderCreate)(void* uptr, int width, int height);
void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data);
void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts);
void (*renderDelete)(void* uptr);
};
```

- **renderCreate** is called to create renderer for specific API, this is where you should create a texture of given size.
- return 1 of success, or 0 on failure.
- **renderUpdate** is called to update texture data
- _rect_ describes the region of the texture that has changed
- _data_ pointer to full texture data
- **renderDraw** is called when the font triangles should be drawn
- _verts_ pointer to vertex position data, 2 floats per vertex
- _tcoords_ pointer to texture coordinate data, 2 floats per vertex
- _colors_ pointer to color data, 1 uint per vertex (or 4 bytes)
- _nverts_ is the number of vertices to draw
- **renderDelete** is called when the renderer should be deleted
- **userPtr** is passed to all calls as first parameter

FontStash uses this API as follows:

```
fonsDrawText() {
foreach (glyph in input string) {
if (internal buffer full) {
updateTexture()
render()
}
add glyph to interal draw buffer
}
updateTexture()
render()
}
```

The size of the internal buffer is defined using `FONS_VERTEX_COUNT` define. The default value is 1024, you can override it when you include fontstash.h and specify the implementation:

``` C
#define FONS_VERTEX_COUNT 2048
#define FONTSTASH_IMPLEMENTATION // Expands implementation
#include "fontstash.h"
```

## Compiling

In order to compile the demo project, your will need to install [GLFW](http://www.glfw.org/) to compile.

FontStash example project uses [premake4](http://industriousone.com/premake) to build platform specific projects, now is good time to install it if you don't have it already. To build the example, navigate into the root folder in your favorite terminal, then:

- *OS X*: `premake4 xcode4`
- *Windows*: `premake4 vs2010`
- *Linux*: `premake4 gmake`

See premake4 documentation for full list of supported build file types. The projects will be created in `build` folder. An example of building and running the example on OS X:

```bash
$ premake4 gmake
$ cd build/
$ make
$ ./example
```

# License
The library is licensed under [zlib license](LICENSE.txt)

## Links
Uses [stb_truetype](http://nothings.org) for font rendering.

BIN
example/FiraSans-Bold.ttf View File


BIN
example/FiraSans-Light.ttf View File


BIN
example/FiraSans-Regular.ttf View File


BIN
example/Roboto-Bold.ttf View File


BIN
example/Roboto-Light.ttf View File


BIN
example/Roboto-Regular.ttf View File


BIN
example/entypo.ttf View File


+ 767
- 0
example/example.c View File

@@ -0,0 +1,767 @@
//
// Copyright (c) 2013 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//

#include <stdio.h>
#include <string.h>
#include <iconv.h>
#include <math.h>
#include <GLFW/glfw3.h>
#include "nanovg.h"
#define GLNANOVG_IMPLEMENTATION
#include "glnanovg.h"

#define ICON_SEARCH 0x1F50D
#define ICON_CIRCLED_CROSS 0x2716
#define ICON_CHEVRON_RIGHT 0xE75E
#define ICON_CHECK 0x2713
#define ICON_LOGIN 0xE740
#define ICON_TRASH 0xE729

static char* cpToUTF8(int cp, char* str)
{
int n = 0;
if (cp < 0x80) n = 1;
else if (cp < 0x800) n = 2;
else if (cp < 0x10000) n = 3;
else if (cp < 0x200000) n = 4;
else if (cp < 0x4000000) n = 5;
else if (cp <= 0x7fffffff) n = 6;
str[n] = '\0';
switch (n) {
case 6: str[5] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x4000000;
case 5: str[4] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x200000;
case 4: str[3] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x10000;
case 3: str[2] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x800;
case 2: str[1] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0xc0;
case 1: str[0] = cp;
}
return str;
}


void drawWindow(struct NVGcontext* vg, const char* title, float x, float y, float w, float h)
{
float cornerRadius = 3.0f;
struct NVGpaint shadowPaint;
struct NVGpaint headerPaint;

nvgSave(vg);
// nvgClearState(vg);

// Window
nvgBeginPath(vg);
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
nvgFillColor(vg, nvgRGBA(28,30,34,192));
// nvgFillColor(vg, nvgRGBA(0,0,0,128));
nvgFill(vg);

// Drop shadow
shadowPaint = nvgBoxGradient(vg, x,y+2, w,h, cornerRadius*2, 10, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
nvgBeginPath(vg);
nvgRect(vg, x-10,y-10, w+20,h+30);
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
nvgPathWinding(vg, NVG_HOLE);
nvgFillPaint(vg, shadowPaint);
nvgFill(vg);

// Header
headerPaint = nvgLinearGradient(vg, x,y,x,y+15, nvgRGBA(255,255,255,8), nvgRGBA(0,0,0,16));
nvgBeginPath(vg);
nvgRoundedRect(vg, x+1,y+1, w-2,30, cornerRadius-1);
nvgFillPaint(vg, headerPaint);
nvgFill(vg);
nvgBeginPath(vg);
nvgMoveTo(vg, x+0.5f, y+0.5f+30);
nvgLineTo(vg, x+0.5f+w-1, y+0.5f+30);
nvgStrokeColor(vg, nvgRGBA(0,0,0,32));
nvgStroke(vg);

nvgFontSize(vg, 18.0f);
nvgFontFace(vg, "sans-bold");
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);

nvgFontBlur(vg,2);
nvgFillColor(vg, nvgRGBA(0,0,0,128));
nvgText(vg, x+w/2,y+16+1, title);

nvgFontBlur(vg,0);
nvgFillColor(vg, nvgRGBA(220,220,220,160));
nvgText(vg, x+w/2,y+16, title);

nvgRestore(vg);
}

void drawSearchBox(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
{
struct NVGpaint bg;
char icon[8];
float cornerRadius = h/2-1;

// Edit
bg = nvgBoxGradient(vg, x,y+1.5f, w,h, h/2,5, nvgRGBA(0,0,0,16), nvgRGBA(0,0,0,92));
nvgBeginPath(vg);
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
nvgFillPaint(vg, bg);
nvgFill(vg);

/* nvgBeginPath(vg);
nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
nvgStroke(vg);*/

nvgFontSize(vg, h*1.3f);
nvgFontFace(vg, "icons");
nvgFillColor(vg, nvgRGBA(255,255,255,64));
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
nvgText(vg, x+h*0.55f, y+h*0.55f, cpToUTF8(ICON_SEARCH,icon));

nvgFontSize(vg, 20.0f);
nvgFontFace(vg, "sans");
nvgFillColor(vg, nvgRGBA(255,255,255,32));

nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
nvgText(vg, x+h*1.05f,y+h*0.5f,text);

nvgFontSize(vg, h*1.3f);
nvgFontFace(vg, "icons");
nvgFillColor(vg, nvgRGBA(255,255,255,32));
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
nvgText(vg, x+w-h*0.55f, y+h*0.55f, cpToUTF8(ICON_CIRCLED_CROSS,icon));
}

void drawDropDown(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
{
struct NVGpaint bg;
char icon[8];
float cornerRadius = 4.0f;

bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16));
nvgBeginPath(vg);
nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1);
nvgFillPaint(vg, bg);
nvgFill(vg);

nvgBeginPath(vg);
nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
nvgStroke(vg);

nvgFontSize(vg, 20.0f);
nvgFontFace(vg, "sans");
nvgFillColor(vg, nvgRGBA(255,255,255,160));
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
nvgText(vg, x+h*0.3f,y+h*0.5f,text);

nvgFontSize(vg, h*1.3f);
nvgFontFace(vg, "icons");
nvgFillColor(vg, nvgRGBA(255,255,255,64));
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
nvgText(vg, x+w-h*0.5f, y+h*0.5f, cpToUTF8(ICON_CHEVRON_RIGHT,icon));
}

void drawLabel(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
{
nvgFontSize(vg, 18.0f);
nvgFontFace(vg, "sans");
nvgFillColor(vg, nvgRGBA(255,255,255,128));

nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
nvgText(vg, x,y+h*0.5f,text);
}

void drawEditBoxBase(struct NVGcontext* vg, float x, float y, float w, float h)
{
struct NVGpaint bg;
// Edit
bg = nvgBoxGradient(vg, x+1,y+1+1.5f, w-2,h-2, 3,4, nvgRGBA(255,255,255,32), nvgRGBA(32,32,32,32));
nvgBeginPath(vg);
nvgRoundedRect(vg, x+1,y+1, w-2,h-2, 4-1);
nvgFillPaint(vg, bg);
nvgFill(vg);

nvgBeginPath(vg);
nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, 4-0.5f);
nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
nvgStroke(vg);
}

void drawEditBox(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
{

drawEditBoxBase(vg, x,y, w,h);

nvgFontSize(vg, 20.0f);
nvgFontFace(vg, "sans");
nvgFillColor(vg, nvgRGBA(255,255,255,64));
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
nvgText(vg, x+h*0.3f,y+h*0.5f,text);
}

void drawEditBoxNum(struct NVGcontext* vg,
const char* text, const char* units, float x, float y, float w, float h)
{
float uw;

drawEditBoxBase(vg, x,y, w,h);

nvgTextBounds(vg, units, &uw, NULL);

nvgFontSize(vg, 18.0f);
nvgFontFace(vg, "sans");
nvgFillColor(vg, nvgRGBA(255,255,255,64));
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
nvgText(vg, x+w-h*0.3f,y+h*0.5f,units);

nvgFontSize(vg, 20.0f);
nvgFontFace(vg, "sans");
nvgFillColor(vg, nvgRGBA(255,255,255,128));
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
nvgText(vg, x+w-uw-h*0.5f,y+h*0.5f,text);
}

void drawCheckBox(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
{
struct NVGpaint bg;
char icon[8];

nvgFontSize(vg, 18.0f);
nvgFontFace(vg, "sans");
nvgFillColor(vg, nvgRGBA(255,255,255,160));

nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
nvgText(vg, x+28,y+h*0.5f,text);

bg = nvgBoxGradient(vg, x+1,y+(int)(h*0.5f)-9+1, 18,18, 3,3, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,92));
nvgBeginPath(vg);
nvgRoundedRect(vg, x+1,y+(int)(h*0.5f)-9, 18,18, 3);
nvgFillPaint(vg, bg);
nvgFill(vg);

nvgFontSize(vg, 40);
nvgFontFace(vg, "icons");
nvgFillColor(vg, nvgRGBA(255,255,255,128));
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
nvgText(vg, x+9+2, y+h*0.5f, cpToUTF8(ICON_CHECK,icon));
}

void drawButton(struct NVGcontext* vg, int preicon, const char* text, float x, float y, float w, float h, unsigned int col)
{
struct NVGpaint bg;
char icon[8];
float cornerRadius = 4.0f;
float tw = 0, iw = 0;

bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,col==0?16:32), nvgRGBA(0,0,0,col==0?16:32));
nvgBeginPath(vg);
nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1);
if (col != 0) {
nvgFillColor(vg, col);
nvgFill(vg);
}
nvgFillPaint(vg, bg);
nvgFill(vg);

nvgBeginPath(vg);
nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
nvgStroke(vg);

nvgFontSize(vg, 20.0f);
nvgFontFace(vg, "sans-bold");
nvgTextBounds(vg, text, &tw, NULL);
if (preicon != 0) {
nvgFontSize(vg, h*1.3f);
nvgFontFace(vg, "icons");
nvgTextBounds(vg, cpToUTF8(preicon,icon), &iw, NULL);
iw += h*0.15f;
}

if (preicon != 0) {
nvgFontSize(vg, h*1.3f);
nvgFontFace(vg, "icons");
nvgFillColor(vg, nvgRGBA(255,255,255,96));
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
nvgText(vg, x+w*0.5f-tw*0.5f-iw*0.75f, y+h*0.5f, cpToUTF8(preicon,icon));
}

nvgFontSize(vg, 20.0f);
nvgFontFace(vg, "sans-bold");
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
nvgFillColor(vg, nvgRGBA(0,0,0,160));
nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f-1,text);
nvgFillColor(vg, nvgRGBA(255,255,255,160));
nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f,text);
}

void drawSlider(struct NVGcontext* vg, float pos, float x, float y, float w, float h)
{
struct NVGpaint bg, knob;
float cy = y+(int)(h*0.5f);
float kr = (int)(h*0.25f);

nvgSave(vg);
// nvgClearState(vg);

// Slot
bg = nvgBoxGradient(vg, x,cy-2+1, w,4, 2,2, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,128));
nvgBeginPath(vg);
nvgRoundedRect(vg, x,cy-2, w,4, 2);
nvgFillPaint(vg, bg);
nvgFill(vg);

// Knob Shadow
bg = nvgRadialGradient(vg, x+(int)(pos*w),cy+1, kr-3,kr+3, nvgRGBA(0,0,0,64), nvgRGBA(0,0,0,0));
nvgBeginPath(vg);
nvgRect(vg, x+(int)(pos*w)-kr-5,cy-kr-5,kr*2+5+5,kr*2+5+5+3);
nvgCircle(vg, x+(int)(pos*w),cy, kr);
nvgPathWinding(vg, NVG_HOLE);
nvgFillPaint(vg, bg);
nvgFill(vg);

// Knob
knob = nvgLinearGradient(vg, x,cy-kr,x,cy+kr, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16));
nvgBeginPath(vg);
nvgCircle(vg, x+(int)(pos*w),cy, kr-1);
nvgFillColor(vg, nvgRGBA(40,43,48,255));
nvgFill(vg);
nvgFillPaint(vg, knob);
nvgFill(vg);

nvgBeginPath(vg);
nvgCircle(vg, x+(int)(pos*w),cy, kr-0.5f);
nvgStrokeColor(vg, nvgRGBA(0,0,0,92));
nvgStroke(vg);

nvgRestore(vg);
}

void drawEyes(struct NVGcontext* vg, float x, float y, float w, float h, float mx, float my, float t)
{
struct NVGpaint gloss, bg;
float ex = w *0.23f;
float ey = h * 0.5f;
float lx = x + ex;
float ly = y + ey;
float rx = x + w - ex;
float ry = y + ey;
float dx,dy,d;
float br = (ex < ey ? ex : ey) * 0.5f;
float blink = 1 - pow(sinf(t*0.5f),200)*0.8f;

bg = nvgLinearGradient(vg, x,y+h*0.5f,x+w*0.1f,y+h, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,16));
nvgBeginPath(vg);
nvgEllipse(vg, lx+3.0f,ly+16.0f, ex,ey);
nvgEllipse(vg, rx+3.0f,ry+16.0f, ex,ey);
nvgFillPaint(vg, bg);
nvgFill(vg);

bg = nvgLinearGradient(vg, x,y+h*0.25f,x+w*0.1f,y+h, nvgRGBA(220,220,220,255), nvgRGBA(128,128,128,255));
nvgBeginPath(vg);
nvgEllipse(vg, lx,ly, ex,ey);
nvgEllipse(vg, rx,ry, ex,ey);
nvgFillPaint(vg, bg);
nvgFill(vg);

dx = (mx - rx) / (ex * 10);
dy = (my - ry) / (ey * 10);
d = sqrtf(dx*dx+dy*dy);
if (d > 1.0f) {
dx /= d; dy /= d;
}
dx *= ex*0.4f;
dy *= ey*0.5f;
nvgBeginPath(vg);
nvgEllipse(vg, lx+dx,ly+dy+ey*0.25f*(1-blink), br,br*blink);
nvgFillColor(vg, nvgRGBA(32,32,32,255));
nvgFill(vg);

dx = (mx - rx) / (ex * 10);
dy = (my - ry) / (ey * 10);
d = sqrtf(dx*dx+dy*dy);
if (d > 1.0f) {
dx /= d; dy /= d;
}
dx *= ex*0.4f;
dy *= ey*0.5f;
nvgBeginPath(vg);
nvgEllipse(vg, rx+dx,ry+dy+ey*0.25f*(1-blink), br,br*blink);
nvgFillColor(vg, nvgRGBA(32,32,32,255));
nvgFill(vg);

gloss = nvgRadialGradient(vg, lx-ex*0.25f,ly-ey*0.5f, ex*0.1f,ex*0.75f, nvgRGBA(255,255,255,128), nvgRGBA(255,255,255,0));
nvgBeginPath(vg);
nvgEllipse(vg, lx,ly, ex,ey);
nvgFillPaint(vg, gloss);
nvgFill(vg);

gloss = nvgRadialGradient(vg, rx-ex*0.25f,ry-ey*0.5f, ex*0.1f,ex*0.75f, nvgRGBA(255,255,255,128), nvgRGBA(255,255,255,0));
nvgBeginPath(vg);
nvgEllipse(vg, rx,ry, ex,ey);
nvgFillPaint(vg, gloss);
nvgFill(vg);
}

void drawGraph(struct NVGcontext* vg, float x, float y, float w, float h, float t)
{
struct NVGpaint bg;
float samples[6];
float sx[6], sy[6];
float dx = w/5.0f;
int i;

samples[0] = (1+sinf(t*1.2345f+cosf(t*0.33457f)*0.44f))*0.5f;
samples[1] = (1+sinf(t*0.68363f+cosf(t*1.3f)*1.55f))*0.5f;
samples[2] = (1+sinf(t*1.1642f+cosf(t*0.33457)*1.24f))*0.5f;
samples[3] = (1+sinf(t*0.56345f+cosf(t*1.63f)*0.14f))*0.5f;
samples[4] = (1+sinf(t*1.6245f+cosf(t*0.254f)*0.3f))*0.5f;
samples[5] = (1+sinf(t*0.345f+cosf(t*0.03f)*0.6f))*0.5f;

for (i = 0; i < 6; i++) {
sx[i] = x+i*dx;
sy[i] = y+h*samples[i]*0.8f;
}

// Graph background
bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(0,160,192,0), nvgRGBA(0,160,192,64));
nvgBeginPath(vg);
nvgMoveTo(vg, sx[0], sy[0]);
for (i = 1; i < 6; i++)
nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
nvgLineTo(vg, x+w, y+h);
nvgLineTo(vg, x, y+h);
nvgFillPaint(vg, bg);
nvgFill(vg);

// Graph line
nvgBeginPath(vg);
nvgMoveTo(vg, sx[0], sy[0]+2);
for (i = 1; i < 6; i++)
nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1]+2, sx[i]-dx*0.5f,sy[i]+2, sx[i],sy[i]+2);
nvgStrokeColor(vg, nvgRGBA(0,0,0,32));
nvgStrokeWidth(vg, 3.0f);
nvgStroke(vg);

nvgBeginPath(vg);
nvgMoveTo(vg, sx[0], sy[0]);
for (i = 1; i < 6; i++)
nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
nvgStrokeColor(vg, nvgRGBA(0,160,192,255));
nvgStrokeWidth(vg, 3.0f);
nvgStroke(vg);

// Graph sample pos
for (i = 0; i < 6; i++) {
bg = nvgRadialGradient(vg, sx[i],sy[i]+2, 3.0f,8.0f, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,0));
nvgBeginPath(vg);
nvgRect(vg, sx[i]-10, sy[i]-10+2, 20,20);
nvgFillPaint(vg, bg);
nvgFill(vg);
}

nvgBeginPath(vg);
for (i = 0; i < 6; i++)
nvgCircle(vg, sx[i], sy[i], 4.0f);
nvgFillColor(vg, nvgRGBA(0,160,192,255));
nvgFill(vg);
nvgBeginPath(vg);
for (i = 0; i < 6; i++)
nvgCircle(vg, sx[i], sy[i], 2.0f);
nvgFillColor(vg, nvgRGBA(220,220,220,255));
nvgFill(vg);

nvgStrokeWidth(vg, 1.0f);
}

void drawThumbnails(struct NVGcontext* vg, float x, float y, float w, float h, const int* images, int nimages, float t)
{
float cornerRadius = 3.0f;
struct NVGpaint shadowPaint, imgPaint, fadePaint;
float ix,iy,iw,ih;
float thumb = 60.0f;
float arry = 30.5f;
int imgw, imgh;
float stackh = (nimages/2) * (thumb+10) + 10;
int i;
float u = (1+cosf(t*0.5f))*0.5f;

nvgSave(vg);
// nvgClearState(vg);

// Drop shadow
shadowPaint = nvgBoxGradient(vg, x,y+4, w,h, cornerRadius*2, 20, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
nvgBeginPath(vg);
nvgRect(vg, x-10,y-10, w+20,h+30);
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
nvgPathWinding(vg, NVG_HOLE);
nvgFillPaint(vg, shadowPaint);
nvgFill(vg);

// Window
nvgBeginPath(vg);
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
nvgMoveTo(vg, x-10,y+arry);
nvgLineTo(vg, x+1,y+arry-11);
nvgLineTo(vg, x+1,y+arry+11);
nvgFillColor(vg, nvgRGBA(200,200,200,255));
nvgFill(vg);

nvgSave(vg);
nvgScissor(vg, x,y,w,h);
nvgTranslate(vg, 0, -(stackh - h)*u);

for (i = 0; i < nimages; i++) {
float tx, ty;
tx = x+10;
ty = y+10;
tx += (i%2) * (thumb+10);
ty += (i/2) * (thumb+10);
nvgImageSize(vg, images[i], &imgw, &imgh);
if (imgw < imgh) {
iw = thumb;
ih = iw * (float)imgh/(float)imgw;
ix = 0;
iy = -(ih-thumb)*0.5f;
} else {
ih = thumb;
iw = ih * (float)imgw/(float)imgh;
ix = -(iw-thumb)*0.5f;
iy = 0;
}
imgPaint = nvgImagePattern(vg, tx+ix, ty+iy, iw,ih, 0.0f/180.0f*NVG_PI, images[i], 0);
nvgBeginPath(vg);
nvgRoundedRect(vg, tx,ty, thumb,thumb, 5);
nvgFillPaint(vg, imgPaint);
nvgFill(vg);

shadowPaint = nvgBoxGradient(vg, tx-1,ty, thumb+2,thumb+2, 5, 3, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
nvgBeginPath(vg);
nvgRect(vg, tx-5,ty-5, thumb+10,thumb+10);
nvgRoundedRect(vg, tx,ty, thumb,thumb, 6);
nvgPathWinding(vg, NVG_HOLE);
nvgFillPaint(vg, shadowPaint);
nvgFill(vg);

nvgBeginPath(vg);
nvgRoundedRect(vg, tx+0.5f,ty+0.5f, thumb-1,thumb-1, 4-0.5f);
nvgStrokeWidth(vg,1.0f);
nvgStrokeColor(vg, nvgRGBA(255,255,255,192));
nvgStroke(vg);
}
nvgRestore(vg);

// Hide fades
fadePaint = nvgLinearGradient(vg, x,y,x,y+6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0));
nvgBeginPath(vg);
nvgRect(vg, x+4,y,w-8,6);
nvgFillPaint(vg, fadePaint);
nvgFill(vg);

fadePaint = nvgLinearGradient(vg, x,y+h,x,y+h-6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0));
nvgBeginPath(vg);
nvgRect(vg, x+4,y+h-6,w-8,6);
nvgFillPaint(vg, fadePaint);
nvgFill(vg);

// Scroll bar
shadowPaint = nvgBoxGradient(vg, x+w-12+1,y+4+1, 8,h-8, 3,4, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,92));
nvgBeginPath(vg);
nvgRoundedRect(vg, x+w-12,y+4, 8,h-8, 3);
nvgFillPaint(vg, shadowPaint);
// nvgFillColor(vg, nvgRGBA(255,0,0,128));
nvgFill(vg);

float scrollh = (h/stackh) * (h-8);
shadowPaint = nvgBoxGradient(vg, x+w-12-1,y+4+(h-8-scrollh)*u-1, 8,scrollh, 3,4, nvgRGBA(220,220,220,255), nvgRGBA(128,128,128,255));
nvgBeginPath(vg);
nvgRoundedRect(vg, x+w-12+1,y+4+1 + (h-8-scrollh)*u, 8-2,scrollh-2, 2);
nvgFillPaint(vg, shadowPaint);
// nvgFillColor(vg, nvgRGBA(0,0,0,128));
nvgFill(vg);

nvgRestore(vg);
}

void errorcb(int error, const char* desc)
{
printf("GLFW error: %s\n", desc);
}

int blowup = 0;

static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
blowup = !blowup;
}

int main()
{
GLFWwindow* window;
int fontNormal = -1, fontBold = -1, fontIcons = -1;
struct NVGcontext* vg = NULL;
int images[12];
int i;

if (!glfwInit()) {
printf("Failed to init GLFW.");
return -1;
}

glfwSetErrorCallback(errorcb);

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);

window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL);
if (!window) {
glfwTerminate();
return -1;
}

glfwSetKeyCallback(window, key);

glfwMakeContextCurrent(window);

vg = glnvgCreate();
if (vg == NULL) {
printf("Could not init nanovg.\n");
return -1;
}

for (i = 0; i < 12; i++) {
char file[128];
snprintf(file, 128, "../example/images/image%d.jpg", i+1);
images[i] = nvgCreateImage(vg, file);
if (images[i] == 0) {
printf("Could not load %s.\n", file);
return -1;
}
}

fontIcons = nvgCreateFont(vg, "icons", "../example/entypo.ttf");
if (fontIcons == -1) {
printf("Could not add font icons.\n");
return -1;
}
fontNormal = nvgCreateFont(vg, "sans", "../example/Roboto-Regular.ttf");
// fontNormal = nvgAddFont(vg, "sans", "../example/FiraSans-Regular.ttf");
if (fontNormal == -1) {
printf("Could not add font italic.\n");
return -1;
}
fontBold = nvgCreateFont(vg, "sans-bold", "../example/Roboto-Bold.ttf");
// fontBold = nvgAddFont(vg, "sans-bold", "../example/FiraSans-Bold.ttf");
if (fontBold == -1) {
printf("Could not add font bold.\n");
return -1;
}


glfwSetTime(0);

while (!glfwWindowShouldClose(window))
{
// float sx, sy, dx, dy, lh = 0;
double mx, my;
int width, height;
glfwGetCursorPos(window, &mx, &my);
glfwGetFramebufferSize(window, &width, &height);
// Update and render
glViewport(0, 0, width, height);
glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,width,height,0,-1,1);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glColor4ub(255,255,255,255);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

float t = glfwGetTime();
float x,y,popy;

nvgBeginFrame(vg);

drawEyes(vg, width - 250, 50, 200, 120, mx, my, t);
drawGraph(vg, 0, height/2, width, height/2, t);

nvgSave(vg);
if (blowup) {
// nvgRotate(vg, 3.0f/180.0f*NVG_PI);
nvgScale(vg, 2.0f, 2.0f);
}

// Widgets
drawWindow(vg, "Widgets `n Stuff", 20, 20, 300, 400);
x = 30; y = 65;
drawSearchBox(vg, "Search", x,y,280,25);
y += 40;
drawDropDown(vg, "Effects", x,y,280,28);
popy = y + 14;
y += 45;

// Form
drawLabel(vg, "Login", x,y, 280,20);
y += 25;
drawEditBox(vg, "Email", x,y, 280,28);
y += 35;
drawEditBox(vg, "Password", x,y, 280,28);
y += 38;
drawCheckBox(vg, "Remember me", x,y, 140,28);
drawButton(vg, ICON_LOGIN, "Sign in", x+138, y, 140, 28, nvgRGBA(0,96,128,255));
y += 45;

// Slider
drawLabel(vg, "Diameter", x,y, 280,20);
y += 25;
drawEditBoxNum(vg, "123.00", "px", x+180,y, 100,28);
drawSlider(vg, 0.4f, x,y, 170,28);
y += 55;

drawButton(vg, ICON_TRASH, "Delete", x, y, 160, 28, nvgRGBA(128,16,8,255));
drawButton(vg, 0, "Cancel", x+170, y, 110, 28, nvgRGBA(0,0,0,0));

// Thumbnails box
drawThumbnails(vg, 325, popy-30, 160, 300, images, 12, t);

nvgRestore(vg);

glEnable(GL_DEPTH_TEST);

glfwSwapBuffers(window);
glfwPollEvents();
}

for (i = 0; i < 12; i++)
nvgDeleteImage(vg, images[i]);

glnvgDelete(vg);

glfwTerminate();
return 0;
}

+ 116
- 0
example/glstash.h View File

@@ -0,0 +1,116 @@
//
// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef GLSTASH_H
#define GLSTASH_H

int glstInit(struct FONSparams* params);
unsigned int glstRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a);

#endif

#ifdef GLSTASH_IMPLEMENTATION

struct GLSTcontext {
GLuint tex;
int width, height;
};

static int glst__renderCreate(void* userPtr, int width, int height)
{
struct GLSTcontext* gl = (struct GLSTcontext*)userPtr;
glGenTextures(1, &gl->tex);
if (!gl->tex) return 0;
gl->width = width;
gl->height = width;
glBindTexture(GL_TEXTURE_2D, gl->tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gl->width, gl->height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
return 1;
}

static void glst__renderUpdate(void* userPtr, int* rect, const unsigned char* data)
{
struct GLSTcontext* gl = (struct GLSTcontext*)userPtr;
if (gl->tex == 0) return;
int w = rect[2] - rect[0];
int h = rect[3] - rect[1];
glBindTexture(GL_TEXTURE_2D, gl->tex);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->width);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect[0]);
glPixelStorei(GL_UNPACK_SKIP_ROWS, rect[1]);
glTexSubImage2D(GL_TEXTURE_2D, 0, rect[0], rect[1], w, h, GL_ALPHA,GL_UNSIGNED_BYTE, data);
}

static void glst__renderDraw(void* userPtr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts)
{
struct GLSTcontext* gl = (struct GLSTcontext*)userPtr;
if (gl->tex == 0) return;
glBindTexture(GL_TEXTURE_2D, gl->tex);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

glVertexPointer(2, GL_FLOAT, sizeof(float)*2, verts);
glTexCoordPointer(2, GL_FLOAT, sizeof(float)*2, tcoords);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(unsigned int), colors);

glDrawArrays(GL_TRIANGLES, 0, nverts);

glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}

static void glst__renderDelete(void* userPtr)
{
struct GLSTcontext* gl = (struct GLSTcontext*)userPtr;
if (gl->tex)
glDeleteTextures(1, &gl->tex);
gl->tex = 0;
free(gl);
}


int glstInit(struct FONSparams* params)
{
struct GLSTcontext* gl = (struct GLSTcontext*)malloc(sizeof(struct GLSTcontext));
if (gl == NULL) goto error;
memset(gl, 0, sizeof(struct GLSTcontext));

params->renderCreate = glst__renderCreate;
params->renderUpdate = glst__renderUpdate;
params->renderDraw = glst__renderDraw;
params->renderDelete = glst__renderDelete;
params->userPtr = gl;

return 1;

error:
if (gl != NULL) free(gl);
return 0;
}

unsigned int glstRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
return (r) | (g << 8) | (b << 16) | (a << 24);
}

#endif

+ 431
- 0
example/icons.txt View File

@@ -0,0 +1,431 @@
struct Icon {
const char* name;
int codepoint;
};


const char* getIcon(const char* name)
{
static struct Icon icons[] = {
{ "phone", 0x1F4DE },
{ "mobile", 0x1F4F1 },
{ "mouse", 0xE789 },
{ "address", 0xE723 },
{ "mail", 0x2709 },
{ "paper-plane", 0x1F53F },
{ "pencil", 0x270E },
{ "feather", 0x2712 },
{ "attach", 0x1F4CE },
{ "inbox", 0xE777 },
{ "reply", 0xE712 },
{ "reply-all", 0xE713 },
{ "forward", 0x27A6 },
{ "user", 0x1F464 },
{ "users", 0x1F465 },
{ "add-user", 0xE700 },
{ "vcard", 0xE722 },
{ "export", 0xE715 },
{ "location", 0xE724 },
{ "map", 0xE727 },
{ "compass", 0xE728 },
{ "direction", 0x27A2 },
{ "hair-cross", 0x1F3AF },
{ "share", 0xE73C },
{ "shareable", 0xE73E },
{ "heart", 0x2665 },
{ "heart-empty", 0x2661 },
{ "star", 0x2605 },
{ "star-empty", 0x2606 },
{ "thumbs-up", 0x1F44D },
{ "thumbs-down", 0x1F44E },
{ "chat", 0xE720 },
{ "comment", 0xE718 },
{ "quote", 0x275E },
{ "home", 0x2302 },
{ "popup", 0xE74C },
{ "search", 0x1F50D },
{ "flashlight", 0x1F526 },
{ "print", 0xE716 },
{ "bell", 0x1F514 },
{ "link", 0x1F517 },
{ "flag", 0x2691 },
{ "cog", 0x2699 },
{ "tools", 0x2692 },
{ "trophy", 0x1F3C6 },
{ "tag", 0xE70C },
{ "camera", 0x1F4F7 },
{ "megaphone", 0x1F4E3 },
{ "moon", 0x263D },
{ "palette", 0x1F3A8 },
{ "leaf", 0x1F342 },
{ "note", 0x266A },
{ "beamed-note", 0x266B },
{ "new", 0x1F4A5 },
{ "graduation-cap", 0x1F393 },
{ "book", 0x1F4D5 },
{ "newspaper", 0x1F4F0 },
{ "bag", 0x1F45C },
{ "airplane", 0x2708 },
{ "lifebuoy", 0xE788 },
{ "eye", 0xE70A },
{ "clock", 0x1F554 },
{ "mic", 0x1F3A4 },
{ "calendar", 0x1F4C5 },
{ "flash", 0x26A1 },
{ "thunder-cloud", 0x26C8 },
{ "droplet", 0x1F4A7 },
{ "cd", 0x1F4BF },
{ "briefcase", 0x1F4BC },
{ "air", 0x1F4A8 },
{ "hourglass", 0x23F3 },
{ "gauge", 0x1F6C7 },
{ "language", 0x1F394 },
{ "network", 0xE776 },
{ "key", 0x1F511 },
{ "battery", 0x1F50B },
{ "bucket", 0x1F4FE },
{ "magnet", 0xE7A1 },
{ "drive", 0x1F4FD },
{ "cup", 0x2615 },
{ "rocket", 0x1F680 },
{ "brush", 0xE79A },
{ "suitcase", 0x1F6C6 },
{ "traffic-cone", 0x1F6C8 },
{ "globe", 0x1F30E },
{ "keyboard", 0x2328 },
{ "browser", 0xE74E },
{ "publish", 0xE74D },
{ "progress-3", 0xE76B },
{ "progress-2", 0xE76A },
{ "progress-1", 0xE769 },
{ "progress-0", 0xE768 },
{ "light-down", 0x1F505 },
{ "light-up", 0x1F506 },
{ "adjust", 0x25D1 },
{ "code", 0xE714 },
{ "monitor", 0x1F4BB },
{ "infinity", 0x221E },
{ "light-bulb", 0x1F4A1 },
{ "credit-card", 0x1F4B3 },
{ "database", 0x1F4F8 },
{ "voicemail", 0x2707 },
{ "clipboard", 0x1F4CB },
{ "cart", 0xE73D },
{ "box", 0x1F4E6 },
{ "ticket", 0x1F3AB },
{ "rss", 0xE73A },
{ "signal", 0x1F4F6 },
{ "thermometer", 0x1F4FF },
{ "water", 0x1F4A6 },
{ "sweden", 0xF601 },
{ "line-graph", 0x1F4C8 },
{ "pie-chart", 0x25F4 },
{ "bar-graph", 0x1F4CA },
{ "area-graph", 0x1F53E },
{ "lock", 0x1F512 },
{ "lock-open", 0x1F513 },
{ "logout", 0xE741 },
{ "login", 0xE740 },
{ "check", 0x2713 },
{ "cross", 0x274C },
{ "squared-minus", 0x229F },
{ "squared-plus", 0x229E },
{ "squared-cross", 0x274E },
{ "circled-minus", 0x2296 },
{ "circled-plus", 0x2295 },
{ "circled-cross", 0x2716 },
{ "minus", 0x2796 },
{ "plus", 0x2795 },
{ "erase", 0x232B },
{ "block", 0x1F6AB },
{ "info", 0x2139 },
{ "circled-info", 0xE705 },
{ "help", 0x2753 },
{ "circled-help", 0xE704 },
{ "warning", 0x26A0 },
{ "cycle", 0x1F504 },
{ "cw", 0x27F3 },
{ "ccw", 0x27F2 },
{ "shuffle", 0x1F500 },
{ "back", 0x1F519 },
{ "level-down", 0x21B3 },
{ "retweet", 0xE717 },
{ "loop", 0x1F501 },
{ "back-in-time", 0xE771 },
{ "level-up", 0x21B0 },
{ "switch", 0x21C6 },
{ "numbered-list", 0xE005 },
{ "add-to-list", 0xE003 },
{ "layout", 0x268F },
{ "list", 0x2630 },
{ "text-doc", 0x1F4C4 },
{ "text-doc-inverted", 0xE731},
{ "doc", 0xE730 },
{ "docs", 0xE736 },
{ "landscape-doc", 0xE737 },
{ "picture", 0x1F304 },
{ "video", 0x1F3AC },
{ "music", 0x1F3B5 },
{ "folder", 0x1F4C1 },
{ "archive", 0xE800 },
{ "trash", 0xE729 },
{ "upload", 0x1F4E4 },
{ "download", 0x1F4E5 },
{ "save", 0x1F4BE },
{ "install", 0xE778 },
{ "cloud", 0x2601 },
{ "upload-cloud", 0xE711 },
{ "bookmark", 0x1F516 },
{ "bookmarks", 0x1F4D1 },
{ "open-book", 0x1F4D6 },
{ "play", 0x25B6 },
{ "paus", 0x2016 },
{ "record", 0x25CF },
{ "stop", 0x25A0 },
{ "ff", 0x23E9 },
{ "fb", 0x23EA },
{ "to-start", 0x23EE },
{ "to-end", 0x23ED },
{ "resize-full", 0xE744 },
{ "resize-small", 0xE746 },
{ "volume", 0x23F7 },
{ "sound", 0x1F50A },
{ "mute", 0x1F507 },
{ "flow-cascade", 0x1F568 },
{ "flow-branch", 0x1F569 },
{ "flow-tree", 0x1F56A },
{ "flow-line", 0x1F56B },
{ "flow-parallel", 0x1F56C },
{ "left-bold", 0xE4AD },
{ "down-bold", 0xE4B0 },
{ "up-bold", 0xE4AF },
{ "right-bold", 0xE4AE },
{ "left", 0x2B05 },
{ "down", 0x2B07 },
{ "up", 0x2B06 },
{ "right", 0x27A1 },
{ "circled-left", 0xE759 },
{ "circled-down", 0xE758 },
{ "circled-up", 0xE75B },
{ "circled-right", 0xE75A },
{ "triangle-left", 0x25C2 },
{ "triangle-down", 0x25BE },
{ "triangle-up", 0x25B4 },
{ "triangle-right", 0x25B8 },
{ "chevron-left", 0xE75D },
{ "chevron-down", 0xE75C },
{ "chevron-up", 0xE75F },
{ "chevron-right", 0xE75E },
{ "chevron-small-left", 0xE761 },
{ "chevron-small-down", 0xE760 },
{ "chevron-small-up", 0xE763 },
{ "chevron-small-right",0xE762 },
{ "chevron-thin-left", 0xE765 },
{ "chevron-thin-down", 0xE764 },
{ "chevron-thin-up", 0xE767 },
{ "chevron-thin-right", 0xE766 },
{ "left-thin", 0x2190 },
{ "down-thin", 0x2193 },
{ "up-thin", 0x2191 },
{ "right-thin", 0x2192 },
{ "arrow-combo", 0xE74F },
{ "three-dots", 0x23F6 },
{ "two-dots", 0x23F5 },
{ "dot", 0x23F4 },
{ "cc", 0x1F545 },
{ "cc-by", 0x1F546 },
{ "cc-nc", 0x1F547 },
{ "cc-nc-eu", 0x1F548 },
{ "cc-nc-jp", 0x1F549 },
{ "cc-sa", 0x1F54A },
{ "cc-nd", 0x1F54B },
{ "cc-pd", 0x1F54C },
{ "cc-zero", 0x1F54D },
{ "cc-share", 0x1F54E },
{ "cc-remix", 0x1F54F },
{ "db-logo", 0x1F5F9 },
{ "db-shape", 0x1F5FA },

/* { "icon-cloud", 0x2601 },
{ "icon-at", 0x0040 },
{ "icon-plus", 0x002B },

{ "icon-arrow_up", 0x2191 },
{ "icon-arrow_down", 0x2193 },
{ "icon-arrow_right", 0x2192 },
{ "icon-arrow_left", 0x2190 },
{ "icon-chevron_down", 0xf004 },
{ "icon-chevron_up", 0xf005 },
{ "icon-chevron_right", 0xf006 },
{ "icon-chevron_left", 0xf007 },
{ "icon-reorder", 0xf008 },
{ "icon-list", 0xf009 },
{ "icon-reorder_square", 0xf00a },
{ "icon-reorder_square_line", 0xf00b },
{ "icon-coverflow", 0xf00c },
{ "icon-coverflow_line", 0xf00d },
{ "icon-pause", 0xf00e },
{ "icon-play", 0xf00f },

{ "icon-step_forward", 0xf010 },
{ "icon-step_backward", 0xf011 },
{ "icon-fast_forward", 0xf012 },
{ "icon-fast_backward", 0xf013 },
{ "icon-cloud_upload", 0xf014 },
{ "icon-cloud_download", 0xf015 },
{ "icon-data_science", 0xf016 },
{ "icon-data_science_black", 0xf017 },
{ "icon-globe", 0xf018 },
{ "icon-globe_black", 0xf019 },
{ "icon-math_ico", 0xf01a },
{ "icon-math", 0xf01b },
{ "icon-math_black", 0xf01c },
{ "icon-paperplane_ico", 0xf01d },
{ "icon-paperplane", 0xf01e },
{ "icon-paperplane_black", 0xf01f },

{ "icon-color_balance", 0xf020 },
{ "icon-star", 0x2605 },
{ "icon-star_half", 0xf022 },
{ "icon-star_empty", 0x2606 },
{ "icon-star_half_empty", 0xf024 },
{ "icon-reload", 0xf025 },

{ "icon-heart", 0x2665 },
{ "icon-heart_broken", 0xf028 },
{ "icon-hashtag", 0xf029 },
{ "icon-reply", 0xf02a },
{ "icon-retweet", 0xf02b },
{ "icon-signin", 0xf02c },
{ "icon-signout", 0xf02d },
{ "icon-download", 0xf02e },
{ "icon-upload", 0xf02f },


{ "icon-placepin", 0xf031 },
{ "icon-display_screen", 0xf032 },
{ "icon-tablet", 0xf033 },
{ "icon-smartphone", 0xf034 },
{ "icon-connected_object", 0xf035 },
{ "icon-lock", 0xF512 },
{ "icon-unlock", 0xF513 },
{ "icon-camera", 0xF4F7 },
{ "icon-isight", 0xf039 },
{ "icon-video_camera", 0xf03a },
{ "icon-random", 0xf03b },
{ "icon-message", 0xF4AC },
{ "icon-discussion", 0xf03d },
{ "icon-calendar", 0xF4C5 },
{ "icon-ringbell", 0xf03f },

{ "icon-movie", 0xf040 },
{ "icon-mail", 0x2709 },
{ "icon-pen", 0x270F },
{ "icon-settings", 0x9881 },
{ "icon-measure", 0xf044 },
{ "icon-vector", 0xf045 },
{ "icon-vector_pen", 0x2712 },
{ "icon-mute_on", 0xf047 },
{ "icon-mute_off", 0xf048 },
{ "icon-home", 0x2302 },
{ "icon-sheet", 0xf04a },
{ "icon-arrow_big_right", 0x21C9 },
{ "icon-arrow_big_left", 0x21C7 },
{ "icon-arrow_big_down", 0x21CA },
{ "icon-arrow_big_up", 0x21C8 },
{ "icon-dribbble_circle", 0xf04f },

{ "icon-dribbble", 0xf050 },
{ "icon-facebook_circle", 0xf051 },
{ "icon-facebook", 0xf052 },
{ "icon-git_circle_alt", 0xf053 },
{ "icon-git_circle", 0xf054 },
{ "icon-git", 0xf055 },
{ "icon-octopus", 0xf056 },
{ "icon-twitter_circle", 0xf057 },
{ "icon-twitter", 0xf058 },
{ "icon-google_plus_circle", 0xf059 },
{ "icon-google_plus", 0xf05a },
{ "icon-linked_in_circle", 0xf05b },
{ "icon-linked_in", 0xf05c },
{ "icon-instagram", 0xf05d },
{ "icon-instagram_circle", 0xf05e },
{ "icon-mfg_icon", 0xf05f },

{ "icon-mfg_icon_circle", 0xf060 },
{ "icon-user", 0xf061 },
{ "icon-user_male", 0xf062 },
{ "icon-user_female", 0xf063 },
{ "icon-users", 0xf064 },

{ "icon-file_open", 0xF4C2 },
{ "icon-file_close", 0xf067 },
{ "icon-file_alt", 0xf068 },
{ "icon-file_close_alt", 0xf069 },
{ "icon-attachment", 0xf06a },
{ "icon-check", 0x2713 },
{ "icon-cross_mark", 0x274C },
{ "icon-cancel_circle", 0xF06E },
{ "icon-check_circle", 0xf06d },
{ "icon-magnifying", 0xF50D },

{ "icon-inbox", 0xf070 },
{ "icon-clock", 0x23F2 },
{ "icon-stopwatch", 0x23F1 },
{ "icon-hourglass", 0x231B },
{ "icon-trophy", 0xf074 },
{ "icon-unlock_alt", 0xF075 },
{ "icon-lock_alt", 0xF510 },
{ "icon-arrow_doubled_right", 0x21D2 },
{ "icon-arrow_doubled_left", 0x21D0 },
{ "icon-arrow_doubled_down", 0x21D3 },
{ "icon-arrow_doubled_up", 0x21D1 },
{ "icon-link", 0xf07B },
{ "icon-warning", 0x2757 },
{ "icon-warning_alt", 0x2755 },
{ "icon-magnifying_plus", 0xf07E },
{ "icon-magnifying_minus", 0xf07F },

{ "icon-white_question", 0x2754 },
{ "icon-black_question", 0x2753 },
{ "icon-stop", 0xf080 },
{ "icon-share", 0xf081 },
{ "icon-eye", 0xf082 },
{ "icon-trash_can", 0xf083 },
{ "icon-hard_drive", 0xf084 },
{ "icon-information_black", 0xf085 },
{ "icon-information_white", 0xf086 },
{ "icon-printer", 0xf087 },
{ "icon-letter", 0xf088 },
{ "icon-soundcloud", 0xf089 },
{ "icon-soundcloud_circle", 0xf08A },
{ "icon-anchor", 0x2693 },

{ "icon-female_sign", 0x2640 },
{ "icon-male_sign", 0x2642 },
{ "icon-joystick", 0xF514 },
{ "icon-high_voltage", 0x26A1 },
{ "icon-fire", 0xF525 },
{ "icon-newspaper", 0xF4F0 },
{ "icon-chart", 0xF526 },
{ "icon-spread", 0xF527 },

{ "icon-spinner_1", 0xF528 },
{ "icon-spinner_2", 0xF529 },

{ "icon-chart_alt", 0xF530 },
{ "icon-label", 0xF531 },*/
};
static const int nicons = sizeof(icons) / sizeof(struct Icon);
int i;
static char str[8];

for (i = 0; i < nicons; i++) {
if (strcmp(icons[i].name, name) == 0) {
cpToUTF8(icons[i].codepoint, str);
return str;
}
}

return "";
}

+ 13
- 0
example/images.txt View File

@@ -0,0 +1,13 @@
Image credits
http://cuteoverload.com/2013/11/05/mom-taxi-xvi-birthday-party/
http://cuteoverload.com/2013/11/05/benson-hedges-private-eye-in-the-case-of-the-crafty-craftsman/
http://cuteoverload.com/2013/11/05/no-underwater-ballets/
http://cuteoverload.com/2013/11/05/every-nose-has-a-story/
http://cuteoverload.com/2013/11/04/nosevember-nozzle-nose/
http://cuteoverload.com/2013/11/04/this-just-in-super-strength-cute/
http://cuteoverload.com/2013/11/03/have-a-bunderful-sunday/
http://cuteoverload.com/2013/11/02/caturday-sense-a-common-theme-here/
http://cuteoverload.com/2013/11/01/nosevember-1st-24-hours-of-noses-1148pm-pt/
http://cuteoverload.com/2013/04/02/there-might-be-something-cuter-than-this/
http://cuteoverload.com/2013/07/17/snorting-micro-peeg-gets-belleh-rubs-interwebs-explode/
http://cuteoverload.com/2013/08/07/bark-in-the-park-v3-0/

BIN
example/images/image1.jpg View File

Before After
Width: 133  |  Height: 100  |  Size: 25KB

BIN
example/images/image10.jpg View File

Before After
Width: 100  |  Height: 100  |  Size: 3.4KB

BIN
example/images/image11.jpg View File

Before After
Width: 109  |  Height: 100  |  Size: 3.7KB

BIN
example/images/image12.jpg View File

Before After
Width: 113  |  Height: 100  |  Size: 5.3KB

BIN
example/images/image2.jpg View File

Before After
Width: 100  |  Height: 101  |  Size: 24KB

BIN
example/images/image3.jpg View File

Before After
Width: 160  |  Height: 100  |  Size: 29KB

BIN
example/images/image4.jpg View File

Before After
Width: 100  |  Height: 116  |  Size: 23KB

BIN
example/images/image5.jpg View File

Before After
Width: 150  |  Height: 100  |  Size: 26KB

BIN
example/images/image6.jpg View File

Before After
Width: 100  |  Height: 104  |  Size: 25KB

BIN
example/images/image7.jpg View File

Before After
Width: 100  |  Height: 117  |  Size: 25KB

BIN
example/images/image8.jpg View File

Before After
Width: 123  |  Height: 100  |  Size: 24KB

BIN
example/images/image9.jpg View File

Before After
Width: 102  |  Height: 100  |  Size: 3.9KB

BIN
example/mfglabsiconset-webfont.ttf View File


+ 84
- 0
example/templates.xml View File

@@ -0,0 +1,84 @@
<style>
$color = rgba(255,255,255,255);
@font-face {
font-family: "Roboto";
font-weight: bold;
src: "../examples/Roboto-light.ttf";
}
@font-face {
font-family: "Entypo";
src: "../examples/entypo.ttf";
}

.icon-search {
font-family: "Entypo";
font-size: 28px;
content: 0x1F50D;
}
.icon-circled-cross {
font-family: "Entypo";
font-size: 28px;
content: 0x2716;
}
.icon-plus {
font-family: "Entypo";
font-size: 28px;
content: "\2796";
}

.search-box {
padding: 5px;
}

.footer-buttons {
padding: 5px;
}

/* default style */
header {
height: 30px;
/*font-size: 26px;*/
}

input {
height: ;
font-size: 26px;
}

</style>

<template id="material-window">
<win width="300px" height="500px" align="justify">
<col align="justify">
<header>Materials</header>
<row height="auto" style="search-box">
<input id="search" grow="1">
<icon style="icon-search" />
<field grow="1" />
<icon style="icon-circled-cross" />
</input>
</row>
<col id="materials" grow="1" height="10px" align="justify" />
<row height="auto" style="footer-buttons">
<spacer grow="1" />
<button id="add-item"><icon style="icon-plus"/>Add</button>
<button id="remove">Remove</button>
</row>
</col>
</win>
</template>

<template id="material-item">
<item padding="4px" align="center">
<img id="thumbnail" width="25px" height="25px" />
<label id="name" grow="1" />
</item>
</template>

<template id="material-noitems">
<col padding="4px" pack="center" align="center">
<icon src="sad-face" />
<label>Sorry, no items found.</label>
</col>
</template>

+ 32
- 0
premake4.lua View File

@@ -0,0 +1,32 @@

local action = _ACTION or ""

solution "nanovg"
location ( "build" )
configurations { "Debug", "Release" }
platforms {"native", "x64", "x32"}
project "example"
kind "ConsoleApp"
language "C"
files { "example/*.c", "src/*.h", "src/*.c" }
includedirs { "src" }
targetdir("build")
configuration { "linux" }
links { "X11","Xrandr", "rt", "GL", "GLU", "pthread" }

configuration { "windows" }
links { "glu32","opengl32", "gdi32", "winmm", "user32" }

configuration { "macosx" }
links { "glfw3" }
linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit" }

configuration "Debug"
defines { "DEBUG" }
flags { "Symbols", "ExtraWarnings"}

configuration "Release"
defines { "NDEBUG" }
flags { "Optimize", "ExtraWarnings"}

+ 1134
- 0
src/fontstash.h
File diff suppressed because it is too large
View File


+ 690
- 0
src/glnanovg.h View File

@@ -0,0 +1,690 @@
//
// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef GLNANOVG_H
#define GLNANOVG_H

struct NVGcontext* glnvgCreate();
void glnvgDelete(struct NVGcontext* ctx);

#endif

#ifdef GLNANOVG_IMPLEMENTATION

#include <stdlib.h>
#include "nanovg.h"

enum GLNVGuniformLoc {
GLNVG_LOC_SCISSORMAT,
GLNVG_LOC_SCISSOREXT,
GLNVG_LOC_PAINTMAT,
GLNVG_LOC_EXTENT,
GLNVG_LOC_RADIUS,
GLNVG_LOC_FEATHER,
GLNVG_LOC_INNERCOL,
GLNVG_LOC_OUTERCOL,
GLNVG_LOC_STROKEMULT,
GLNVG_LOC_TEX,
GLNVG_MAX_LOCS
};

struct GLNVGshader {
GLuint prog;
GLuint frag;
GLuint vert;
GLint loc[GLNVG_MAX_LOCS];
};

struct GLNVGtexture {
int id;
GLuint tex;
int width, height;
int type;
};

struct GLNVGcontext {
struct GLNVGshader gradShader;
struct GLNVGshader imgShader;
struct GLNVGtexture* textures;
int ntextures;
int ctextures;
int textureId;
};

static struct GLNVGtexture* glnvg__allocTexture(struct GLNVGcontext* gl)
{
struct GLNVGtexture* tex = NULL;
int i;

for (i = 0; i < gl->ntextures; i++) {
if (gl->textures[i].id == 0) {
tex = &gl->textures[i];
break;
}
}
if (tex == NULL) {
if (gl->ntextures+1 > gl->ctextures) {
gl->ctextures = (gl->ctextures == 0) ? 2 : gl->ctextures*2;
gl->textures = (struct GLNVGtexture*)realloc(gl->textures, sizeof(struct GLNVGtexture)*gl->ctextures);
if (gl->textures == NULL) return NULL;
}
tex = &gl->textures[gl->ntextures++];
}
memset(tex, 0, sizeof(*tex));
tex->id = ++gl->textureId;
return tex;
}

static struct GLNVGtexture* glnvg__findTexture(struct GLNVGcontext* gl, int id)
{
int i;
for (i = 0; i < gl->ntextures; i++)
if (gl->textures[i].id == id)
return &gl->textures[i];
return NULL;
}

static int glnvg__deleteTexture(struct GLNVGcontext* gl, int id)
{
int i;
for (i = 0; i < gl->ntextures; i++) {
if (gl->textures[i].id == id) {
if (gl->textures[i].tex != 0)
glDeleteTextures(1, &gl->textures[i].tex);
memset(&gl->textures[i], 0, sizeof(gl->textures[i]));
return 1;
}
}
return 0;
}

static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type)
{
char str[512+1];
int len = 0;
glGetShaderInfoLog(shader, 512, &len, str);
if (len > 512) len = 512;
str[len] = '\0';
printf("Shader %s/%s error:\n%s\n", name, type, str);
}

static void glnvg__dumpProgramError(GLuint prog, const char* name)
{
char str[512+1];
int len = 0;
glGetProgramInfoLog(prog, 512, &len, str);
if (len > 512) len = 512;
str[len] = '\0';
printf("Program %s error:\n%s\n", name, str);
}

static void glnvg__checkError(const char* str)
{
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
printf("Error %08x after %s\n", err, str);
}
}

static int glnvg__createShader(struct GLNVGshader* shader, const char* name, const char* vshader, const char* fshader)
{
GLint status;
GLuint prog, vert, frag;

memset(shader, 0, sizeof(*shader));

prog = glCreateProgram();
vert = glCreateShader(GL_VERTEX_SHADER);
frag = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vert, 1, &vshader, 0);
glShaderSource(frag, 1, &fshader, 0);

glCompileShader(vert);
glGetShaderiv(vert, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
glnvg__dumpShaderError(vert, name, "vert");
return 0;
}

glCompileShader(frag);
glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
glnvg__dumpShaderError(frag, name, "frag");
return 0;
}

glAttachShader(prog, vert);
glAttachShader(prog, frag);
glLinkProgram(prog);
glGetProgramiv(prog, GL_LINK_STATUS, &status);
if (status != GL_TRUE) {
glnvg__dumpProgramError(prog, name);
return 0;
}

shader->prog = prog;
shader->vert = vert;
shader->frag = frag;

return 1;
}

static void glnvg__deleteShader(struct GLNVGshader* shader)
{
if (shader->prog != 0)
glDeleteProgram(shader->prog);
if (shader->vert != 0)
glDeleteShader(shader->vert);
if (shader->frag != 0)
glDeleteShader(shader->frag);
}

static int glnvg__renderCreate(void* uptr)
{
struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr;

static const char* fillVertShader =
"#version 120\n"
"varying vec2 pos;\n"
"varying vec2 alpha;\n"
"void main(void) {\n"
" alpha = gl_MultiTexCoord0.st;"
" pos = gl_Vertex.xy;\n"
" gl_Position = ftransform();\n"
"}\n";

static const char* fillFragGradShader =
"#version 120\n"
"uniform mat3 scissorMat;\n"
"uniform vec2 scissorExt;\n"
"uniform mat3 paintMat;\n"
"uniform vec2 extent;\n"
"uniform float radius;\n"
"uniform float feather;\n"
"uniform vec4 innerCol;\n"
"uniform vec4 outerCol;\n"
"uniform float strokeMult;\n"
"varying vec2 pos;\n"
"varying vec2 alpha;\n"
"float sdroundrect(vec2 pt, vec2 ext, float rad) {\n"
" vec2 ext2 = ext - vec2(rad,rad);\n"
" vec2 d = abs(pt) - ext2;\n"
" return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n"
"}\n"
"void main(void) {\n"
" // Scissoring\n"
" vec2 sc = vec2(0.5,0.5) - (abs((scissorMat * vec3(pos,1.0)).xy) - scissorExt);\n"
" float scissor = clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n"
// " if (scissor < 0.001) discard;\n"
" // Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n"
" float strokeAlpha = min(1.0, (1.0-abs(alpha.x*2.0-1.0))*strokeMult) * alpha.y;\n"
" // Calculate gradient color using box gradient\n"
" vec2 pt = (paintMat * vec3(pos,1.0)).xy;\n"
" float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n"
" vec4 color = mix(innerCol,outerCol,d);\n"
" // Combine alpha\n"
" color.w *= strokeAlpha * scissor;\n"
" gl_FragColor = color;\n"
"}\n";

static const char* fillFragImgShader =
"#version 120\n"
"uniform mat3 scissorMat;\n"
"uniform vec2 scissorExt;\n"
"uniform mat3 paintMat;\n"
"uniform vec2 extent;\n"
"uniform float strokeMult;\n"
"uniform sampler2D tex;\n"
"varying vec2 pos;\n"
"varying vec2 alpha;\n"
"void main(void) {\n"
" // Scissoring\n"
" vec2 sc = vec2(0.5,0.5) - (abs((scissorMat * vec3(pos,1.0)).xy) - scissorExt);\n"
" float scissor = clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n"
// " if (scissor < 0.001) discard;\n"
" // Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n"
" float strokeAlpha = min(1.0, (1.0-abs(alpha.x*2.0-1.0))*strokeMult) * alpha.y;\n"
" // Calculate color fron texture\n"
" vec2 pt = (paintMat * vec3(pos,1.0)).xy;\n"
" pt /= extent;\n"
" vec4 color = texture2D(tex, pt);\n"
" // Combine alpha\n"
" color.w *= strokeAlpha * scissor;\n"
" gl_FragColor = color;\n"
"}\n";

glnvg__checkError("init");

if (glnvg__createShader(&gl->gradShader, "grad", fillVertShader, fillFragGradShader) == 0)
return 0;

glnvg__checkError("grad");

gl->gradShader.loc[GLNVG_LOC_SCISSORMAT] = glGetUniformLocation(gl->gradShader.prog, "scissorMat");
gl->gradShader.loc[GLNVG_LOC_SCISSOREXT] = glGetUniformLocation(gl->gradShader.prog, "scissorExt");
gl->gradShader.loc[GLNVG_LOC_PAINTMAT] = glGetUniformLocation(gl->gradShader.prog, "paintMat");
gl->gradShader.loc[GLNVG_LOC_EXTENT] = glGetUniformLocation(gl->gradShader.prog, "extent");
gl->gradShader.loc[GLNVG_LOC_RADIUS] = glGetUniformLocation(gl->gradShader.prog, "radius");
gl->gradShader.loc[GLNVG_LOC_FEATHER] = glGetUniformLocation(gl->gradShader.prog, "feather");
gl->gradShader.loc[GLNVG_LOC_INNERCOL] = glGetUniformLocation(gl->gradShader.prog, "innerCol");
gl->gradShader.loc[GLNVG_LOC_OUTERCOL] = glGetUniformLocation(gl->gradShader.prog, "outerCol");
gl->gradShader.loc[GLNVG_LOC_STROKEMULT] = glGetUniformLocation(gl->gradShader.prog, "strokeMult");

glnvg__checkError("grad loc");

if (glnvg__createShader(&gl->imgShader, "image", fillVertShader, fillFragImgShader) == 0)
return 0;

glnvg__checkError("image");

gl->imgShader.loc[GLNVG_LOC_SCISSORMAT] = glGetUniformLocation(gl->imgShader.prog, "scissorMat");
gl->imgShader.loc[GLNVG_LOC_SCISSOREXT] = glGetUniformLocation(gl->imgShader.prog, "scissorExt");
gl->imgShader.loc[GLNVG_LOC_PAINTMAT] = glGetUniformLocation(gl->imgShader.prog, "paintMat");
gl->imgShader.loc[GLNVG_LOC_EXTENT] = glGetUniformLocation(gl->imgShader.prog, "extent");
// gl->gradShader.loc[GLNVG_LOC_RADIUS] = glGetUniformLocation(gl->gradShader.prog, "radius");
// gl->gradShader.loc[GLNVG_LOC_FEATHER] = glGetUniformLocation(gl->gradShader.prog, "feather");
// gl->gradShader.loc[GLNVG_LOC_INNERCOL] = glGetUniformLocation(gl->gradShader.prog, "innerCol");
// gl->gradShader.loc[GLNVG_LOC_OUTERCOL] = glGetUniformLocation(gl->gradShader.prog, "outerCol");
gl->imgShader.loc[GLNVG_LOC_STROKEMULT] = glGetUniformLocation(gl->imgShader.prog, "strokeMult");
gl->imgShader.loc[GLNVG_LOC_TEX] = glGetUniformLocation(gl->imgShader.prog, "tex");

glnvg__checkError("image loc");

return 1;
}

static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, const unsigned char* data)
{
struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr;
struct GLNVGtexture* tex = glnvg__allocTexture(gl);
if (tex == NULL) return 0;
glGenTextures(1, &tex->tex);
tex->width = w;
tex->height = h;
tex->type = type;
glBindTexture(GL_TEXTURE_2D, tex->tex);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);

if (type == NVG_TEXTURE_RGBA)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glnvg__checkError("create tex");

return tex->id;
}

static int glnvg__renderDeleteTexture(void* uptr, int image)
{
struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr;
return glnvg__deleteTexture(gl, image);
}

static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data)
{
struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr;
struct GLNVGtexture* tex = glnvg__findTexture(gl, image);
if (tex == NULL) return 0;
glBindTexture(GL_TEXTURE_2D, tex->tex);

glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, x);
glPixelStorei(GL_UNPACK_SKIP_ROWS, y);

if (tex->type == NVG_TEXTURE_RGBA)
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data);
else
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_ALPHA, GL_UNSIGNED_BYTE, data);

return 1;
}

static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h)
{
struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr;
struct GLNVGtexture* tex = glnvg__findTexture(gl, image);
if (tex == NULL) return 0;
*w = tex->width;
*h = tex->height;
return 1;
}

static void glnvg__toFloatColor(float* fc, unsigned int c)
{
fc[0] = ((c) & 0xff) / 255.0f;
fc[1] = ((c>>8) & 0xff) / 255.0f;
fc[2] = ((c>>16) & 0xff) / 255.0f;
fc[3] = ((c>>24) & 0xff) / 255.0f;
}

static void glnvg__xformIdentity(float* t)
{
t[0] = 1.0f; t[1] = 0.0f;
t[2] = 0.0f; t[3] = 1.0f;
t[4] = 0.0f; t[5] = 0.0f;
}

static void glnvg__xformInverse(float* inv, float* t)
{
double det = (double)t[0] * t[3] - (double)t[2] * t[1];
if (det > -1e-6 && det < -1e-6) {
glnvg__xformIdentity(t);
return;
}
double invdet = 1.0 / det;
inv[0] = (float)(t[3] * invdet);
inv[2] = (float)(-t[2] * invdet);
inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
inv[1] = (float)(-t[1] * invdet);
inv[3] = (float)(t[0] * invdet);
inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
}

static void glnvg__xformToMat3x3(float* m3, float* t)
{
m3[0] = t[0];
m3[1] = t[1];
m3[2] = 0.0f;
m3[3] = t[2];
m3[4] = t[3];
m3[5] = 0.0f;
m3[6] = t[4];
m3[7] = t[5];
m3[8] = 1.0f;
}

static int glnvg__setupPaint(struct GLNVGcontext* gl, struct NVGpaint* paint, struct NVGscissor* scissor,
float width, float aasize)
{
float innerCol[4];
float outerCol[4];
glnvg__toFloatColor(innerCol, paint->innerColor);
glnvg__toFloatColor(outerCol, paint->outerColor);
struct GLNVGtexture* tex = NULL;
float invxform[6], paintMat[9], scissorMat[9];
float scissorx = 0, scissory = 0;

glnvg__xformInverse(invxform, paint->xform);
glnvg__xformToMat3x3(paintMat, invxform);

if (scissor->extent[0] < 0.5f || scissor->extent[1] < 0.5f) {
memset(scissorMat, 0, sizeof(scissorMat));
scissorx = 1.0f;
scissory = 1.0f;
} else {
glnvg__xformInverse(invxform, scissor->xform);
glnvg__xformToMat3x3(scissorMat, invxform);
scissorx = scissor->extent[0];
scissory = scissor->extent[1];
}

if (paint->image != 0) {
tex = glnvg__findTexture(gl, paint->image);
if (tex == NULL) return 0;
glUseProgram(gl->imgShader.prog);
glUniformMatrix3fv(gl->imgShader.loc[GLNVG_LOC_SCISSORMAT], 1, GL_FALSE, scissorMat);
glUniform2f(gl->imgShader.loc[GLNVG_LOC_SCISSOREXT], scissorx, scissory);
glUniformMatrix3fv(gl->imgShader.loc[GLNVG_LOC_PAINTMAT], 1, GL_FALSE, paintMat);
glUniform2f(gl->imgShader.loc[GLNVG_LOC_EXTENT], paint->extent[0], paint->extent[1]);
glUniform1f(gl->imgShader.loc[GLNVG_LOC_STROKEMULT], width*0.5f + aasize*0.5f);
glUniform1i(gl->imgShader.loc[GLNVG_LOC_TEX], 0);

glnvg__checkError("tex paint loc");

glBindTexture(GL_TEXTURE_2D, tex->tex);
glEnable(GL_TEXTURE_2D);

glnvg__checkError("tex paint tex");

} else {
glUseProgram(gl->gradShader.prog);
glUniformMatrix3fv(gl->gradShader.loc[GLNVG_LOC_SCISSORMAT], 1, GL_FALSE, scissorMat);
glUniform2f(gl->gradShader.loc[GLNVG_LOC_SCISSOREXT], scissorx, scissory);
glUniformMatrix3fv(gl->gradShader.loc[GLNVG_LOC_PAINTMAT], 1, GL_FALSE, paintMat);
glUniform2f(gl->gradShader.loc[GLNVG_LOC_EXTENT], paint->extent[0], paint->extent[1]);
glUniform1f(gl->gradShader.loc[GLNVG_LOC_RADIUS], paint->radius);
glUniform1f(gl->gradShader.loc[GLNVG_LOC_FEATHER], paint->feather);
glUniform4fv(gl->gradShader.loc[GLNVG_LOC_INNERCOL], 1, innerCol);