| @@ -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; | |||