@@ -0,0 +1,28 @@ | |||
name: CI | |||
on: [push] | |||
env: | |||
BUILD_TYPE: release | |||
jobs: | |||
build: | |||
runs-on: ${{ matrix.os }} | |||
strategy: | |||
matrix: | |||
os: [ ubuntu-latest] | |||
steps: | |||
- uses: actions/checkout@v2 | |||
- name: Install required packages | |||
run: sudo apt-get install premake4 build-essential libglfw3-dev libglew-dev pkg-config | |||
- name: Premake | |||
run: premake4 gmake | |||
# due to glew problems with in the current ubuntu-latest, we don't build the examples (yet) | |||
# https://github.com/openai/mujoco-py/issues/383 has the same problem for reference | |||
# this doesn't happen in focal | |||
- name: Make | |||
run: cd build && make nanovg |
@@ -1,3 +1,5 @@ | |||
*This project is not actively maintained.* | |||
NanoVG | |||
========== | |||
@@ -111,6 +113,10 @@ See the header file [nanovg.h](/src/nanovg.h) for API reference. | |||
## License | |||
The library is licensed under [zlib license](LICENSE.txt) | |||
Fonts used in examples: | |||
- Roboto licensed under [Apache license](http://www.apache.org/licenses/LICENSE-2.0) | |||
- Entypo licensed under CC BY-SA 4.0. | |||
- Noto Emoji licensed under [SIL Open Font License, Version 1.1](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) | |||
## Discussions | |||
[NanoVG mailing list](https://groups.google.com/forum/#!forum/nanovg) | |||
@@ -25,7 +25,7 @@ | |||
#define ICON_TRASH 0xE729 | |||
//static float minf(float a, float b) { return a < b ? a : b; } | |||
static float maxf(float a, float b) { return a > b ? a : b; } | |||
//static float maxf(float a, float b) { return a > b ? a : b; } | |||
//static float absf(float a) { return a >= 0.0f ? a : -a; } | |||
static float clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } | |||
@@ -98,7 +98,7 @@ void drawWindow(NVGcontext* vg, const char* title, float x, float y, float w, fl | |||
nvgStrokeColor(vg, nvgRGBA(0,0,0,32)); | |||
nvgStroke(vg); | |||
nvgFontSize(vg, 18.0f); | |||
nvgFontSize(vg, 15.0f); | |||
nvgFontFace(vg, "sans-bold"); | |||
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); | |||
@@ -137,7 +137,7 @@ void drawSearchBox(NVGcontext* vg, const char* text, float x, float y, float w, | |||
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); | |||
nvgText(vg, x+h*0.55f, y+h*0.55f, cpToUTF8(ICON_SEARCH,icon), NULL); | |||
nvgFontSize(vg, 20.0f); | |||
nvgFontSize(vg, 17.0f); | |||
nvgFontFace(vg, "sans"); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,32)); | |||
@@ -168,7 +168,7 @@ void drawDropDown(NVGcontext* vg, const char* text, float x, float y, float w, f | |||
nvgStrokeColor(vg, nvgRGBA(0,0,0,48)); | |||
nvgStroke(vg); | |||
nvgFontSize(vg, 20.0f); | |||
nvgFontSize(vg, 17.0f); | |||
nvgFontFace(vg, "sans"); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,160)); | |||
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); | |||
@@ -185,7 +185,7 @@ void drawLabel(NVGcontext* vg, const char* text, float x, float y, float w, floa | |||
{ | |||
NVG_NOTUSED(w); | |||
nvgFontSize(vg, 18.0f); | |||
nvgFontSize(vg, 15.0f); | |||
nvgFontFace(vg, "sans"); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,128)); | |||
@@ -214,7 +214,7 @@ void drawEditBox(NVGcontext* vg, const char* text, float x, float y, float w, fl | |||
drawEditBoxBase(vg, x,y, w,h); | |||
nvgFontSize(vg, 20.0f); | |||
nvgFontSize(vg, 17.0f); | |||
nvgFontFace(vg, "sans"); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,64)); | |||
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); | |||
@@ -230,13 +230,13 @@ void drawEditBoxNum(NVGcontext* vg, | |||
uw = nvgTextBounds(vg, 0,0, units, NULL, NULL); | |||
nvgFontSize(vg, 18.0f); | |||
nvgFontSize(vg, 15.0f); | |||
nvgFontFace(vg, "sans"); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,64)); | |||
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); | |||
nvgText(vg, x+w-h*0.3f,y+h*0.5f,units, NULL); | |||
nvgFontSize(vg, 20.0f); | |||
nvgFontSize(vg, 17.0f); | |||
nvgFontFace(vg, "sans"); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,128)); | |||
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); | |||
@@ -249,7 +249,7 @@ void drawCheckBox(NVGcontext* vg, const char* text, float x, float y, float w, f | |||
char icon[8]; | |||
NVG_NOTUSED(w); | |||
nvgFontSize(vg, 18.0f); | |||
nvgFontSize(vg, 15.0f); | |||
nvgFontFace(vg, "sans"); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,160)); | |||
@@ -262,7 +262,7 @@ void drawCheckBox(NVGcontext* vg, const char* text, float x, float y, float w, f | |||
nvgFillPaint(vg, bg); | |||
nvgFill(vg); | |||
nvgFontSize(vg, 40); | |||
nvgFontSize(vg, 33); | |||
nvgFontFace(vg, "icons"); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,128)); | |||
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); | |||
@@ -291,7 +291,7 @@ void drawButton(NVGcontext* vg, int preicon, const char* text, float x, float y, | |||
nvgStrokeColor(vg, nvgRGBA(0,0,0,48)); | |||
nvgStroke(vg); | |||
nvgFontSize(vg, 20.0f); | |||
nvgFontSize(vg, 17.0f); | |||
nvgFontFace(vg, "sans-bold"); | |||
tw = nvgTextBounds(vg, 0,0, text, NULL, NULL); | |||
if (preicon != 0) { | |||
@@ -309,7 +309,7 @@ void drawButton(NVGcontext* vg, int preicon, const char* text, float x, float y, | |||
nvgText(vg, x+w*0.5f-tw*0.5f-iw*0.75f, y+h*0.5f, cpToUTF8(preicon,icon), NULL); | |||
} | |||
nvgFontSize(vg, 20.0f); | |||
nvgFontSize(vg, 17.0f); | |||
nvgFontFace(vg, "sans-bold"); | |||
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); | |||
nvgFillColor(vg, nvgRGBA(0,0,0,160)); | |||
@@ -868,13 +868,15 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, | |||
float caretx, px; | |||
float bounds[4]; | |||
float a; | |||
const char* hoverText = "Hover your mouse over the text to see calculated caret position."; | |||
float gx,gy; | |||
int gutter = 0; | |||
const char* boxText = "Testing\nsome multiline\ntext."; | |||
NVG_NOTUSED(height); | |||
nvgSave(vg); | |||
nvgFontSize(vg, 18.0f); | |||
nvgFontSize(vg, 15.0f); | |||
nvgFontFace(vg, "sans"); | |||
nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); | |||
nvgTextMetrics(vg, NULL, NULL, &lineh); | |||
@@ -891,7 +893,7 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, | |||
nvgBeginPath(vg); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,hit?64:16)); | |||
nvgRect(vg, x, y, row->width, lineh); | |||
nvgRect(vg, x + row->minx, y, row->maxx - row->minx, lineh); | |||
nvgFill(vg); | |||
nvgFillColor(vg, nvgRGBA(255,255,255,255)); | |||
@@ -928,7 +930,7 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, | |||
if (gutter) { | |||
char txt[16]; | |||
snprintf(txt, sizeof(txt), "%d", gutter); | |||
nvgFontSize(vg, 13.0f); | |||
nvgFontSize(vg, 12.0f); | |||
nvgTextAlign(vg, NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); | |||
nvgTextBounds(vg, gx,gy, txt, NULL, bounds); | |||
@@ -944,16 +946,16 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, | |||
y += 20.0f; | |||
nvgFontSize(vg, 13.0f); | |||
nvgFontSize(vg, 11.0f); | |||
nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); | |||
nvgTextLineHeight(vg, 1.2f); | |||
nvgTextBoxBounds(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL, bounds); | |||
nvgTextBoxBounds(vg, x,y, 150, hoverText, NULL, bounds); | |||
// Fade the tooltip out when close to it. | |||
gx = fabsf((mx - (bounds[0]+bounds[2])*0.5f) / (bounds[0] - bounds[2])); | |||
gy = fabsf((my - (bounds[1]+bounds[3])*0.5f) / (bounds[1] - bounds[3])); | |||
a = maxf(gx, gy) - 0.5f; | |||
gx = clampf(mx, bounds[0], bounds[2]) - mx; | |||
gy = clampf(my, bounds[1], bounds[3]) - my; | |||
a = sqrtf(gx*gx + gy*gy) / 30.0f; | |||
a = clampf(a, 0, 1); | |||
nvgGlobalAlpha(vg, a); | |||
@@ -967,7 +969,7 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, | |||
nvgFill(vg); | |||
nvgFillColor(vg, nvgRGBA(0,0,0,220)); | |||
nvgTextBox(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL); | |||
nvgTextBox(vg, x,y, 150, hoverText, NULL); | |||
nvgRestore(vg); | |||
} | |||
@@ -151,36 +151,36 @@ void renderGraph(NVGcontext* vg, float x, float y, PerfGraph* fps) | |||
nvgFontFace(vg, "sans"); | |||
if (fps->name[0] != '\0') { | |||
nvgFontSize(vg, 14.0f); | |||
nvgFontSize(vg, 12.0f); | |||
nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); | |||
nvgFillColor(vg, nvgRGBA(240,240,240,192)); | |||
nvgText(vg, x+3,y+1, fps->name, NULL); | |||
nvgText(vg, x+3,y+3, fps->name, NULL); | |||
} | |||
if (fps->style == GRAPH_RENDER_FPS) { | |||
nvgFontSize(vg, 18.0f); | |||
nvgFontSize(vg, 15.0f); | |||
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); | |||
nvgFillColor(vg, nvgRGBA(240,240,240,255)); | |||
sprintf(str, "%.2f FPS", 1.0f / avg); | |||
nvgText(vg, x+w-3,y+1, str, NULL); | |||
nvgText(vg, x+w-3,y+3, str, NULL); | |||
nvgFontSize(vg, 15.0f); | |||
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_BOTTOM); | |||
nvgFontSize(vg, 13.0f); | |||
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_BASELINE); | |||
nvgFillColor(vg, nvgRGBA(240,240,240,160)); | |||
sprintf(str, "%.2f ms", avg * 1000.0f); | |||
nvgText(vg, x+w-3,y+h-1, str, NULL); | |||
nvgText(vg, x+w-3,y+h-3, str, NULL); | |||
} | |||
else if (fps->style == GRAPH_RENDER_PERCENT) { | |||
nvgFontSize(vg, 18.0f); | |||
nvgFontSize(vg, 15.0f); | |||
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); | |||
nvgFillColor(vg, nvgRGBA(240,240,240,255)); | |||
sprintf(str, "%.1f %%", avg * 1.0f); | |||
nvgText(vg, x+w-3,y+1, str, NULL); | |||
nvgText(vg, x+w-3,y+3, str, NULL); | |||
} else { | |||
nvgFontSize(vg, 18.0f); | |||
nvgFontSize(vg, 15.0f); | |||
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); | |||
nvgFillColor(vg, nvgRGBA(240,240,240,255)); | |||
sprintf(str, "%.2f ms", avg * 1000.0f); | |||
nvgText(vg, x+w-3,y+1, str, NULL); | |||
nvgText(vg, x+w-3,y+3, str, NULL); | |||
} | |||
} |
@@ -102,8 +102,8 @@ int fonsExpandAtlas(FONScontext* s, int width, int height); | |||
int fonsResetAtlas(FONScontext* stash, int width, int height); | |||
// Add fonts | |||
int fonsAddFont(FONScontext* s, const char* name, const char* path); | |||
int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData); | |||
int fonsAddFont(FONScontext* s, const char* name, const char* path, int fontIndex); | |||
int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData, int fontIndex); | |||
int fonsGetFontByName(FONScontext* s, const char* name); | |||
// State handling | |||
@@ -175,13 +175,13 @@ int fons__tt_done(FONScontext *context) | |||
return ftError == 0; | |||
} | |||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) | |||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) | |||
{ | |||
FT_Error ftError; | |||
FONS_NOTUSED(context); | |||
//font->font.userdata = stash; | |||
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); | |||
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font); | |||
return ftError == 0; | |||
} | |||
@@ -212,7 +212,7 @@ int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float | |||
ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); | |||
if (ftError) return 0; | |||
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); | |||
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT); | |||
if (ftError) return 0; | |||
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed); | |||
if (ftError) return 0; | |||
@@ -231,7 +231,7 @@ void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int | |||
{ | |||
FT_GlyphSlot ftGlyph = font->font->glyph; | |||
int ftGlyphOffset = 0; | |||
int x, y; | |||
unsigned int x, y; | |||
FONS_NOTUSED(outWidth); | |||
FONS_NOTUSED(outHeight); | |||
FONS_NOTUSED(scaleX); | |||
@@ -278,13 +278,18 @@ int fons__tt_done(FONScontext *context) | |||
return 1; | |||
} | |||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) | |||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) | |||
{ | |||
int stbError; | |||
int offset, stbError; | |||
FONS_NOTUSED(dataSize); | |||
font->font.userdata = context; | |||
stbError = stbtt_InitFont(&font->font, data, 0); | |||
offset = stbtt_GetFontOffsetForIndex(data, fontIndex); | |||
if (offset == -1) { | |||
stbError = 0; | |||
} else { | |||
stbError = stbtt_InitFont(&font->font, data, offset); | |||
} | |||
return stbError; | |||
} | |||
@@ -792,6 +797,17 @@ int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) | |||
return 0; | |||
} | |||
void fonsResetFallbackFont(FONScontext* stash, int base) | |||
{ | |||
int i; | |||
FONSfont* baseFont = stash->fonts[base]; | |||
baseFont->nfallbacks = 0; | |||
baseFont->nglyphs = 0; | |||
for (i = 0; i < FONS_HASH_LUT_SIZE; i++) | |||
baseFont->lut[i] = -1; | |||
} | |||
void fonsSetSize(FONScontext* stash, float size) | |||
{ | |||
fons__getState(stash)->size = size; | |||
@@ -890,7 +906,7 @@ error: | |||
return FONS_INVALID; | |||
} | |||
int fonsAddFont(FONScontext* stash, const char* name, const char* path) | |||
int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex) | |||
{ | |||
FILE* fp = 0; | |||
int dataSize = 0; | |||
@@ -908,9 +924,9 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path) | |||
readed = fread(data, 1, dataSize, fp); | |||
fclose(fp); | |||
fp = 0; | |||
if (readed != dataSize) goto error; | |||
if (readed != (size_t)dataSize) goto error; | |||
return fonsAddFontMem(stash, name, data, dataSize, 1); | |||
return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex); | |||
error: | |||
if (data) free(data); | |||
@@ -918,7 +934,7 @@ error: | |||
return FONS_INVALID; | |||
} | |||
int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData) | |||
int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex) | |||
{ | |||
int i, ascent, descent, fh, lineGap; | |||
FONSfont* font; | |||
@@ -943,15 +959,16 @@ int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, in | |||
// Init font | |||
stash->nscratch = 0; | |||
if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; | |||
if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error; | |||
// Store normalized line height. The real line height is got | |||
// by multiplying the lineh by font size. | |||
fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); | |||
ascent += lineGap; | |||
fh = ascent - descent; | |||
font->ascender = (float)ascent / (float)fh; | |||
font->descender = (float)descent / (float)fh; | |||
font->lineh = (float)(fh + lineGap) / (float)fh; | |||
font->lineh = font->ascender - font->descender; | |||
return idx; | |||
@@ -1208,8 +1225,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, | |||
y1 = (float)(glyph->y1-1); | |||
if (stash->params.flags & FONS_ZERO_TOPLEFT) { | |||
rx = (float)(int)(*x + xoff); | |||
ry = (float)(int)(*y + yoff); | |||
rx = floorf(*x + xoff); | |||
ry = floorf(*y + yoff); | |||
q->x0 = rx; | |||
q->y0 = ry; | |||
@@ -1221,8 +1238,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, | |||
q->s1 = x1 * stash->itw; | |||
q->t1 = y1 * stash->ith; | |||
} else { | |||
rx = (float)(int)(*x + xoff); | |||
ry = (float)(int)(*y - yoff); | |||
rx = floorf(*x + xoff); | |||
ry = floorf(*y - yoff); | |||
q->x0 = rx; | |||
q->y0 = ry; | |||
@@ -24,8 +24,11 @@ | |||
#include "nanovg.h" | |||
#define FONTSTASH_IMPLEMENTATION | |||
#include "fontstash.h" | |||
#ifndef NVG_NO_STB | |||
#define STB_IMAGE_IMPLEMENTATION | |||
#include "stb_image.h" | |||
#endif | |||
#ifdef _MSC_VER | |||
#pragma warning(disable: 4100) // unreferenced formal parameter | |||
@@ -74,7 +77,7 @@ struct NVGstate { | |||
float miterLimit; | |||
int lineJoin; | |||
int lineCap; | |||
float alpha; | |||
NVGcolor tint; | |||
float xform[6]; | |||
NVGscissor scissor; | |||
float fontSize; | |||
@@ -651,7 +654,7 @@ void nvgReset(NVGcontext* ctx) | |||
state->miterLimit = 10.0f; | |||
state->lineCap = NVG_BUTT; | |||
state->lineJoin = NVG_MITER; | |||
state->alpha = 1.0f; | |||
state->tint = nvgRGBAf(1, 1, 1, 1); | |||
nvgTransformIdentity(state->xform); | |||
state->scissor.extent[0] = -1.0f; | |||
@@ -697,9 +700,20 @@ void nvgLineJoin(NVGcontext* ctx, int join) | |||
} | |||
void nvgGlobalAlpha(NVGcontext* ctx, float alpha) | |||
{ | |||
nvgGlobalTint(ctx, nvgRGBAf(1, 1, 1, alpha)); | |||
} | |||
void nvgGlobalTint(NVGcontext* ctx, NVGcolor tint) | |||
{ | |||
NVGstate* state = nvg__getState(ctx); | |||
state->tint = tint; | |||
} | |||
NVGcolor nvgGetGlobalTint(NVGcontext* ctx) | |||
{ | |||
NVGstate* state = nvg__getState(ctx); | |||
state->alpha = alpha; | |||
return state->tint; | |||
} | |||
void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f) | |||
@@ -788,6 +802,7 @@ void nvgFillPaint(NVGcontext* ctx, NVGpaint paint) | |||
nvgTransformMultiply(state->fill.xform, state->xform); | |||
} | |||
#ifndef NVG_NO_STB | |||
int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) | |||
{ | |||
int w, h, n, image; | |||
@@ -816,6 +831,7 @@ int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int | |||
stbi_image_free(img); | |||
return image; | |||
} | |||
#endif | |||
int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data) | |||
{ | |||
@@ -2229,9 +2245,11 @@ void nvgFill(NVGcontext* ctx) | |||
else | |||
nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); | |||
// Apply global alpha | |||
fillPaint.innerColor.a *= state->alpha; | |||
fillPaint.outerColor.a *= state->alpha; | |||
// Apply global tint | |||
for (i = 0; i < 4; i++) { | |||
fillPaint.innerColor.rgba[i] *= state->tint.rgba[i]; | |||
fillPaint.outerColor.rgba[i] *= state->tint.rgba[i]; | |||
} | |||
ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, | |||
ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); | |||
@@ -2264,9 +2282,11 @@ void nvgStroke(NVGcontext* ctx) | |||
strokeWidth = ctx->fringeWidth; | |||
} | |||
// Apply global alpha | |||
strokePaint.innerColor.a *= state->alpha; | |||
strokePaint.outerColor.a *= state->alpha; | |||
// Apply global tint | |||
for (i = 0; i < 4; i++) { | |||
strokePaint.innerColor.rgba[i] *= state->tint.rgba[i]; | |||
strokePaint.outerColor.rgba[i] *= state->tint.rgba[i]; | |||
} | |||
nvg__flattenPaths(ctx); | |||
@@ -2287,14 +2307,24 @@ void nvgStroke(NVGcontext* ctx) | |||
} | |||
// Add fonts | |||
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* path) | |||
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename) | |||
{ | |||
return fonsAddFont(ctx->fs, name, filename, 0); | |||
} | |||
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex) | |||
{ | |||
return fonsAddFont(ctx->fs, name, path); | |||
return fonsAddFont(ctx->fs, name, filename, fontIndex); | |||
} | |||
int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData) | |||
{ | |||
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData); | |||
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, 0); | |||
} | |||
int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex) | |||
{ | |||
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, fontIndex); | |||
} | |||
int nvgFindFont(NVGcontext* ctx, const char* name) | |||
@@ -2315,6 +2345,16 @@ int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallba | |||
return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont)); | |||
} | |||
void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont) | |||
{ | |||
fonsResetFallbackFont(ctx->fs, baseFont); | |||
} | |||
void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont) | |||
{ | |||
nvgResetFallbackFontsId(ctx, nvgFindFont(ctx, baseFont)); | |||
} | |||
// State setting | |||
void nvgFontSize(NVGcontext* ctx, float size) | |||
{ | |||
@@ -2413,22 +2453,31 @@ static int nvg__allocTextAtlas(NVGcontext* ctx) | |||
static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) | |||
{ | |||
int i; | |||
NVGstate* state = nvg__getState(ctx); | |||
NVGpaint paint = state->fill; | |||
// Render triangles. | |||
paint.image = ctx->fontImages[ctx->fontImageIdx]; | |||
// Apply global alpha | |||
paint.innerColor.a *= state->alpha; | |||
paint.outerColor.a *= state->alpha; | |||
// Apply global tint | |||
for (i = 0; i < 4; i++) { | |||
paint.innerColor.rgba[i] *= state->tint.rgba[i]; | |||
paint.outerColor.rgba[i] *= state->tint.rgba[i]; | |||
} | |||
ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts); | |||
ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth); | |||
ctx->drawCallCount++; | |||
ctx->textTriCount += nverts/3; | |||
} | |||
static int nvg__isTransformFlipped(const float *xform) | |||
{ | |||
float det = xform[0] * xform[3] - xform[2] * xform[1]; | |||
return( det < 0); | |||
} | |||
float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end) | |||
{ | |||
NVGstate* state = nvg__getState(ctx); | |||
@@ -2439,6 +2488,7 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* | |||
float invscale = 1.0f / scale; | |||
int cverts = 0; | |||
int nverts = 0; | |||
int isFlipped = nvg__isTransformFlipped(state->xform); | |||
if (end == NULL) | |||
end = string + strlen(string); | |||
@@ -2472,6 +2522,12 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* | |||
break; | |||
} | |||
prevIter = iter; | |||
if(isFlipped) { | |||
float tmp; | |||
tmp = q.y0; q.y0 = q.y1; q.y1 = tmp; | |||
tmp = q.t0; q.t0 = q.t1; q.t1 = tmp; | |||
} | |||
// Transform corners. | |||
nvgTransformPoint(&c[0],&c[1], state->xform, q.x0*invscale, q.y0*invscale); | |||
nvgTransformPoint(&c[2],&c[3], state->xform, q.x1*invscale, q.y0*invscale); | |||
@@ -2684,7 +2740,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||
rowStartX = iter.x; | |||
rowStart = iter.str; | |||
rowEnd = iter.next; | |||
rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX; | |||
rowWidth = iter.nextx - rowStartX; | |||
rowMinX = q.x0 - rowStartX; | |||
rowMaxX = q.x1 - rowStartX; | |||
wordStart = iter.str; | |||
@@ -2714,7 +2770,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||
if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) { | |||
wordStart = iter.str; | |||
wordStartX = iter.x; | |||
wordMinX = q.x0 - rowStartX; | |||
wordMinX = q.x0; | |||
} | |||
// Break to new line when a character is beyond break width. | |||
@@ -2751,13 +2807,13 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||
nrows++; | |||
if (nrows >= maxRows) | |||
return nrows; | |||
// Update row | |||
rowStartX = wordStartX; | |||
rowStart = wordStart; | |||
rowEnd = iter.next; | |||
rowWidth = iter.nextx - rowStartX; | |||
rowMinX = wordMinX; | |||
rowMinX = wordMinX - rowStartX; | |||
rowMaxX = q.x1 - rowStartX; | |||
// No change to the word start | |||
} | |||
// Set null break point | |||
breakEnd = rowStart; | |||
@@ -271,6 +271,8 @@ void nvgLineJoin(NVGcontext* ctx, int join); | |||
// Sets the transparency applied to all rendered shapes. | |||
// Already transparent paths will get proportionally more transparent as well. | |||
void nvgGlobalAlpha(NVGcontext* ctx, float alpha); | |||
void nvgGlobalTint(NVGcontext* ctx, NVGcolor tint); | |||
NVGcolor nvgGetGlobalTint(NVGcontext* ctx); | |||
// | |||
// Transforms | |||
@@ -414,7 +416,7 @@ NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, | |||
NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, | |||
NVGcolor icol, NVGcolor ocol); | |||
// Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern, | |||
// Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern, | |||
// (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. | |||
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). | |||
NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, | |||
@@ -546,10 +548,16 @@ void nvgStroke(NVGcontext* ctx); | |||
// Returns handle to the font. | |||
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); | |||
// fontIndex specifies which font face to load from a .ttf/.ttc file. | |||
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex); | |||
// Creates font by loading it from the specified memory chunk. | |||
// Returns handle to the font. | |||
int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); | |||
// fontIndex specifies which font face to load from a .ttf/.ttc file. | |||
int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex); | |||
// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. | |||
int nvgFindFont(NVGcontext* ctx, const char* name); | |||
@@ -559,6 +567,12 @@ int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont); | |||
// Adds a fallback font by name. | |||
int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont); | |||
// Resets fallback fonts by handle. | |||
void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont); | |||
// Resets fallback fonts by name. | |||
void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont); | |||
// Sets the font size of current text style. | |||
void nvgFontSize(NVGcontext* ctx, float size); | |||
@@ -658,7 +672,7 @@ struct NVGparams { | |||
void (*renderFlush)(void* uptr); | |||
void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); | |||
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); | |||
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts); | |||
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe); | |||
void (*renderDelete)(void* uptr); | |||
}; | |||
typedef struct NVGparams NVGparams; | |||
@@ -268,6 +268,8 @@ struct GLNVGcontext { | |||
GLuint stencilFuncMask; | |||
GLNVGblend blendFunc; | |||
#endif | |||
int dummyTex; | |||
}; | |||
typedef struct GLNVGcontext GLNVGcontext; | |||
@@ -500,6 +502,8 @@ static void glnvg__getUniforms(GLNVGshader* shader) | |||
#endif | |||
} | |||
static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); | |||
static int glnvg__renderCreate(void* uptr) | |||
{ | |||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | |||
@@ -700,6 +704,10 @@ static int glnvg__renderCreate(void* uptr) | |||
#endif | |||
gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; | |||
// Some platforms does not allow to have samples to unset textures. | |||
// Create empty one which is bound when there's no texture specified. | |||
gl->dummyTex = glnvg__renderCreateTexture(gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); | |||
glnvg__checkError(gl, "create done"); | |||
glFinish(); | |||
@@ -973,6 +981,7 @@ static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i); | |||
static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | |||
{ | |||
GLNVGtexture* tex = NULL; | |||
#if NANOVG_GL_USE_UNIFORMBUFFER | |||
glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); | |||
#else | |||
@@ -981,12 +990,14 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | |||
#endif | |||
if (image != 0) { | |||
GLNVGtexture* tex = glnvg__findTexture(gl, image); | |||
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); | |||
glnvg__checkError(gl, "tex paint tex"); | |||
} else { | |||
glnvg__bindTexture(gl, 0); | |||
tex = glnvg__findTexture(gl, image); | |||
} | |||
// If no image is set, use empty texture | |||
if (tex == NULL) { | |||
tex = glnvg__findTexture(gl, gl->dummyTex); | |||
} | |||
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); | |||
glnvg__checkError(gl, "tex paint tex"); | |||
} | |||
static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) | |||
@@ -1481,7 +1492,7 @@ error: | |||
} | |||
static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, | |||
const NVGvertex* verts, int nverts) | |||
const NVGvertex* verts, int nverts, float fringe) | |||
{ | |||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | |||
GLNVGcall* call = glnvg__allocCall(gl); | |||
@@ -1504,7 +1515,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOper | |||
call->uniformOffset = glnvg__allocFragUniforms(gl, 1); | |||
if (call->uniformOffset == -1) goto error; | |||
frag = nvg__fragUniformPtr(gl, call->uniformOffset); | |||
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f); | |||
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f); | |||
frag->type = NSVG_SHADER_IMG; | |||
return; | |||