diff --git a/example/demo.c b/example/demo.c index 33316229..7a9c7c55 100644 --- a/example/demo.c +++ b/example/demo.c @@ -360,6 +360,35 @@ void drawSlider(NVGcontext* vg, float pos, float x, float y, float w, float h) nvgRestore(vg); } +void drawFancyText(NVGcontext* vg, float x, float y){ + nvgFontSize(vg, 30.0f); + nvgFontFace(vg, "sans-bold"); + nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); + + nvgFontBlur(vg, 10); + nvgFillColor(vg, nvgRGB(255, 0, 102)); + nvgText(vg, x, y, "Font Blur", NULL); + nvgFontBlur(vg,0); + nvgFillColor(vg, nvgRGB(255,255,255)); + nvgText(vg, x, y, "Font Blur", NULL); + + nvgFontDilate(vg, 2); + nvgFillColor(vg, nvgRGB(255, 0, 102)); + nvgText(vg, x, y+40, "Font Outline", NULL); + nvgFontDilate(vg,0); + nvgFillColor(vg, nvgRGB(255,255,255)); + nvgText(vg, x, y+40, "Font Outline", NULL); + + nvgFontDilate(vg, 3); // Dilate will always be applied before blur + nvgFontBlur(vg, 2); + nvgFillColor(vg, nvgRGB(255, 0, 102)); + nvgText(vg, x, y+80, "Font Blur Outline", NULL); + nvgFontDilate(vg,0); + nvgFontBlur(vg,0); + nvgFillColor(vg, nvgRGB(255,255,255)); + nvgText(vg, x, y+80, "Font Blur Outline", NULL); +} + void drawEyes(NVGcontext* vg, float x, float y, float w, float h, float mx, float my, float t) { NVGpaint gloss, bg; @@ -1068,6 +1097,7 @@ void renderDemo(NVGcontext* vg, float mx, float my, float width, float height, float x,y,popy; drawEyes(vg, width - 250, 50, 150, 100, mx, my, t); + drawFancyText(vg, width - 175, 190); drawParagraph(vg, width - 450, 50, 150, 100, mx, my); drawGraph(vg, 0, height/2, width, height/2, t); drawColorwheel(vg, width - 300, height - 300, 250.0f, 250.0f, t); diff --git a/src/fontstash.h b/src/fontstash.h index b3feeaab..45e8d977 100644 --- a/src/fontstash.h +++ b/src/fontstash.h @@ -76,7 +76,7 @@ typedef struct FONSquad FONSquad; struct FONStextIter { float x, y, nextx, nexty, scale, spacing; unsigned int codepoint; - short isize, iblur; + short isize, iblur, idilate; struct FONSfont* font; int prevGlyphIndex; const char* str; @@ -116,6 +116,7 @@ void fonsSetSize(FONScontext* s, float size); void fonsSetColor(FONScontext* s, unsigned int color); void fonsSetSpacing(FONScontext* s, float spacing); void fonsSetBlur(FONScontext* s, float blur); +void fonsSetDilate(FONScontext* s, float dilate); void fonsSetAlign(FONScontext* s, int align); void fonsSetFont(FONScontext* s, int font); @@ -225,7 +226,7 @@ struct FONSglyph unsigned int codepoint; int index; int next; - short size, blur; + short size, blur, dilate; short x0,y0,x1,y1; short xadv,xoff,yoff; }; @@ -257,6 +258,7 @@ struct FONSstate float size; unsigned int color; float blur; + float dilate; float spacing; }; typedef struct FONSstate FONSstate; @@ -834,6 +836,10 @@ void fonsSetBlur(FONScontext* stash, float blur) { fons__getState(stash)->blur = blur; } +void fonsSetDilate(FONScontext* stash, float dilate) +{ + fons__getState(stash)->dilate = dilate; +} void fonsSetAlign(FONScontext* stash, int align) { @@ -874,6 +880,7 @@ void fonsClearState(FONScontext* stash) state->color = 0xffffffff; state->font = 0; state->blur = 0; + state->dilate= 0; state->spacing = 0; state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; } @@ -1069,12 +1076,143 @@ static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int fons__blurCols(dst, w, h, dstStride, alpha); fons__blurRows(dst, w, h, dstStride, alpha); fons__blurCols(dst, w, h, dstStride, alpha); -// fons__blurrows(dst, w, h, dstStride, alpha); -// fons__blurcols(dst, w, h, dstStride, alpha); +} + +static void fons__maxRows(unsigned char* dst, int w, int h, int dstStride) +{ + int x, y; + unsigned char prev, current; + unsigned char* ptr; + for (x = 0; x < w; x++) { + prev=dst[0]; + for (y = dstStride; y < h*dstStride; y += dstStride) { + ptr=&dst[y]; + current=*ptr; + if(prev > current){ + *ptr=prev; + } + prev=current; + } + for (y = (h-2)*dstStride; y >= 0; y -= dstStride) { + ptr=&dst[y]; + current=*ptr; + if(prev > current){ + *ptr=prev; + } + prev=current; + } + dst++; + } +} + +static void fons__maxCols(unsigned char* dst, int w, int h, int dstStride) +{ + int x, y; + unsigned char prev, current; + unsigned char* ptr; + for (y = 0; y < h; y++) { + prev=dst[0]; + for (x = 1; x < w; x++) { + ptr=&dst[x]; + current=*ptr; + if(prev > current){ + *ptr=prev; + } + prev=current; + } + for (x = w-2; x >= 0; x--) { + ptr=&dst[x]; + current=*ptr; + if(prev > current){ + *ptr=prev; + } + prev=current; + } + dst += dstStride; + } +} + +static void fons__maxDiagUp(unsigned char* dst, int w, int h, int dstStride) +{ + int t, y; + const int a =dstStride-1; + const int d=w+h; + unsigned char prev, current; + unsigned char* ptr; + for(t=0;t current){ + *ptr=prev; + } + prev=current; + } + for(y=y_max-1;y>=y_min;y--){ + ptr=&dst[t+y*a]; + current=*ptr; + if(prev > current){ + *ptr=prev; + } + prev=current; + } + } +} + +static void fons__maxDiagDown(unsigned char* dst, int w, int h, int dstStride) +{ + int t, y; + const int a=(h-1)*dstStride; + const int b=dstStride+1; + const int d=w+h; + unsigned char prev, current; + unsigned char* ptr; + for(t=0;t current){ + *ptr=prev; + } + prev=current; + } + for(y=y_max-1;y>=y_min;y--){ + ptr=&dst[t-y*b+a]; + current=*ptr; + if(prev > current){ + *ptr=prev; + } + prev=current; + } + } +} + +// Gray level morphological dilation approximated by convolving with a max stencil along +// Diagonal convolution overlaps with horizontal & vertical, so we alternate between vertical & horizontal +// and diagonal directions to prevent the dilation from being too large. +static void fons__dilate(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int dilate) +{ + (void)stash; + + for(int iter=0;iter 20) iblur = 20; - pad = iblur+2; + if (idilate > 20) idilate = 20; + const int antiAliasBonus = 2; + pad = antiAliasBonus + iblur + idilate; // Reset allocator. stash->nscratch = 0; @@ -1097,7 +1237,9 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); i = font->lut[h]; while (i != -1) { - if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) { + if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize + && font->glyphs[i].blur == iblur + && font->glyphs[i].dilate == idilate) { glyph = &font->glyphs[i]; if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || (glyph->x0 >= 0 && glyph->y0 >= 0)) { return glyph; @@ -1151,6 +1293,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in glyph->codepoint = codepoint; glyph->size = isize; glyph->blur = iblur; + glyph->dilate = idilate; glyph->next = 0; // Insert char to hash lookup. @@ -1195,6 +1338,13 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in } }*/ + // Dilate + if (idilate > 0) { + stash->nscratch = 0; + bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; + fons__dilate(stash, bdst, gw, gh, stash->params.width, idilate); + } + // Blur if (iblur > 0) { stash->nscratch = 0; @@ -1331,6 +1481,7 @@ float fonsDrawText(FONScontext* stash, int prevGlyphIndex = -1; short isize = (short)(state->size*10.0f); short iblur = (short)state->blur; + short idilate = (short)state->dilate; float scale; FONSfont* font; float width; @@ -1361,7 +1512,7 @@ float fonsDrawText(FONScontext* stash, for (; str != end; ++str) { if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) continue; - glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED); + glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, idilate, FONS_GLYPH_BITMAP_REQUIRED); if (glyph != NULL) { fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); @@ -1398,6 +1549,7 @@ int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, iter->isize = (short)(state->size*10.0f); iter->iblur = (short)state->blur; + iter->idilate = (short)state->dilate; iter->scale = fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize/10.0f); // Align horizontally @@ -1445,7 +1597,7 @@ int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) // Get glyph and quad iter->x = iter->nextx; iter->y = iter->nexty; - glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur, iter->bitmapOption); + glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur, iter->idilate, iter->bitmapOption); // If the iterator was initialized with FONS_GLYPH_BITMAP_OPTIONAL, then the UV coordinates of the quad will be invalid. if (glyph != NULL) fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); @@ -1518,6 +1670,7 @@ float fonsTextBounds(FONScontext* stash, int prevGlyphIndex = -1; short isize = (short)(state->size*10.0f); short iblur = (short)state->blur; + short idilate = (short)state->dilate; float scale; FONSfont* font; float startx, advance; @@ -1543,7 +1696,7 @@ float fonsTextBounds(FONScontext* stash, for (; str != end; ++str) { if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) continue; - glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL); + glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, idilate, FONS_GLYPH_BITMAP_OPTIONAL); if (glyph != NULL) { fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); if (q.x0 < minx) minx = q.x0; diff --git a/src/nanovg.c b/src/nanovg.c index 61826e84..fa391276 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -84,6 +84,7 @@ struct NVGstate { float letterSpacing; float lineHeight; float fontBlur; + float fontDilate; int textAlign; int fontId; }; @@ -664,6 +665,7 @@ void nvgReset(NVGcontext* ctx) state->letterSpacing = 0.0f; state->lineHeight = 1.0f; state->fontBlur = 0.0f; + state->fontDilate = 0.0f; state->textAlign = NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE; state->fontId = 0; } @@ -2353,6 +2355,12 @@ void nvgFontBlur(NVGcontext* ctx, float blur) state->fontBlur = blur; } +void nvgFontDilate(NVGcontext* ctx, float dilate) +{ + NVGstate* state = nvg__getState(ctx); + state->fontDilate = dilate; +} + void nvgTextLetterSpacing(NVGcontext* ctx, float spacing) { NVGstate* state = nvg__getState(ctx); @@ -2480,6 +2488,7 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* fonsSetSize(ctx->fs, state->fontSize*scale); fonsSetSpacing(ctx->fs, state->letterSpacing*scale); fonsSetBlur(ctx->fs, state->fontBlur*scale); + fonsSetDilate(ctx->fs, state->fontDilate*scale); fonsSetAlign(ctx->fs, state->textAlign); fonsSetFont(ctx->fs, state->fontId); @@ -2651,6 +2660,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa fonsSetSize(ctx->fs, state->fontSize*scale); fonsSetSpacing(ctx->fs, state->letterSpacing*scale); fonsSetBlur(ctx->fs, state->fontBlur*scale); + fonsSetDilate(ctx->fs, state->fontDilate*scale); fonsSetAlign(ctx->fs, state->textAlign); fonsSetFont(ctx->fs, state->fontId); @@ -2835,6 +2845,7 @@ float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const fonsSetSize(ctx->fs, state->fontSize*scale); fonsSetSpacing(ctx->fs, state->letterSpacing*scale); fonsSetBlur(ctx->fs, state->fontBlur*scale); + fonsSetDilate(ctx->fs, state->fontDilate*scale); fonsSetAlign(ctx->fs, state->textAlign); fonsSetFont(ctx->fs, state->fontId); @@ -2879,6 +2890,7 @@ void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, co fonsSetSize(ctx->fs, state->fontSize*scale); fonsSetSpacing(ctx->fs, state->letterSpacing*scale); fonsSetBlur(ctx->fs, state->fontBlur*scale); + fonsSetDilate(ctx->fs, state->fontDilate*scale); fonsSetAlign(ctx->fs, state->textAlign); fonsSetFont(ctx->fs, state->fontId); fonsLineBounds(ctx->fs, 0, &rminy, &rmaxy); @@ -2930,6 +2942,7 @@ void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* l fonsSetSize(ctx->fs, state->fontSize*scale); fonsSetSpacing(ctx->fs, state->letterSpacing*scale); fonsSetBlur(ctx->fs, state->fontBlur*scale); + fonsSetDilate(ctx->fs, state->fontDilate*scale); fonsSetAlign(ctx->fs, state->textAlign); fonsSetFont(ctx->fs, state->fontId); diff --git a/src/nanovg.h b/src/nanovg.h index cb63e52e..26705ba6 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -577,6 +577,9 @@ void nvgFontSize(NVGcontext* ctx, float size); // Sets the blur of current text style. void nvgFontBlur(NVGcontext* ctx, float blur); +// Sets the dilation of current text style. +void nvgFontDilate(NVGcontext* ctx, float dilate); + // Sets the letter spacing of current text style. void nvgTextLetterSpacing(NVGcontext* ctx, float spacing);