diff options
-rw-r--r-- | src/ui/text.c | 49 |
1 files changed, 29 insertions, 20 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index 1761a87c..a8be33b6 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -85,7 +85,7 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) | |||
85 | struct Impl_Font { | 85 | struct Impl_Font { |
86 | iBlock * data; | 86 | iBlock * data; |
87 | stbtt_fontinfo font; | 87 | stbtt_fontinfo font; |
88 | float scale; | 88 | float xScale, yScale; |
89 | int vertOffset; /* offset due to scaling */ | 89 | int vertOffset; /* offset due to scaling */ |
90 | int height; | 90 | int height; |
91 | int baseline; | 91 | int baseline; |
@@ -100,21 +100,33 @@ struct Impl_Font { | |||
100 | 100 | ||
101 | static iFont *font_Text_(enum iFontId id); | 101 | static iFont *font_Text_(enum iFontId id); |
102 | 102 | ||
103 | static void init_Font(iFont *d, const iBlock *data, int height, float scale, enum iFontId symbolsFont) { | 103 | static void init_Font(iFont *d, const iBlock *data, int height, float scale, |
104 | enum iFontId symbolsFont, iBool isMonospaced) { | ||
104 | init_Hash(&d->glyphs); | 105 | init_Hash(&d->glyphs); |
105 | d->data = NULL; | 106 | d->data = NULL; |
107 | d->isMonospaced = isMonospaced; | ||
106 | d->height = height; | 108 | d->height = height; |
107 | iZap(d->font); | 109 | iZap(d->font); |
108 | stbtt_InitFont(&d->font, constData_Block(data), 0); | 110 | stbtt_InitFont(&d->font, constData_Block(data), 0); |
109 | d->scale = stbtt_ScaleForPixelHeight(&d->font, height) * scale; | 111 | d->xScale = d->yScale = stbtt_ScaleForPixelHeight(&d->font, height) * scale; |
112 | if (d->isMonospaced) { | ||
113 | /* It is important that monospaced fonts align 1:1 with the pixel grid so that | ||
114 | box-drawing characters don't have partially occupied edge pixels, leading to seams | ||
115 | between adjacent glyphs. */ | ||
116 | int adv; | ||
117 | stbtt_GetCodepointHMetrics(&d->font, 'M', &adv, NULL); | ||
118 | const float advance = (float) adv * d->xScale; | ||
119 | if (advance > 4) { /* not too tiny */ | ||
120 | d->xScale *= floorf(advance) / advance; | ||
121 | } | ||
122 | } | ||
110 | d->vertOffset = height * (1.0f - scale) / 2; | 123 | d->vertOffset = height * (1.0f - scale) / 2; |
111 | int ascent; | 124 | int ascent; |
112 | stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL); | 125 | stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL); |
113 | d->baseline = ceil(ascent * d->scale); | 126 | d->baseline = ceil(ascent * d->yScale); |
114 | d->symbolsFont = symbolsFont; | 127 | d->symbolsFont = symbolsFont; |
115 | d->japaneseFont = regularJapanese_FontId; | 128 | d->japaneseFont = regularJapanese_FontId; |
116 | d->koreanFont = regularKorean_FontId; | 129 | d->koreanFont = regularKorean_FontId; |
117 | d->isMonospaced = iFalse; | ||
118 | memset(d->indexTable, 0xff, sizeof(d->indexTable)); | 130 | memset(d->indexTable, 0xff, sizeof(d->indexTable)); |
119 | } | 131 | } |
120 | 132 | ||
@@ -271,11 +283,12 @@ static void initFonts_Text_(iText *d) { | |||
271 | }; | 283 | }; |
272 | iForIndices(i, fontData) { | 284 | iForIndices(i, fontData) { |
273 | iFont *font = &d->fonts[i]; | 285 | iFont *font = &d->fonts[i]; |
274 | init_Font( | 286 | init_Font(font, |
275 | font, fontData[i].ttf, fontData[i].size, fontData[i].scaling, fontData[i].symbolsFont); | 287 | fontData[i].ttf, |
276 | if (fontData[i].ttf == &fontFiraMonoRegular_Embedded) { | 288 | fontData[i].size, |
277 | font->isMonospaced = iTrue; | 289 | fontData[i].scaling, |
278 | } | 290 | fontData[i].symbolsFont, |
291 | fontData[i].ttf == &fontFiraMonoRegular_Embedded); | ||
279 | if (i == default_FontId || i == defaultMedium_FontId) { | 292 | if (i == default_FontId || i == defaultMedium_FontId) { |
280 | font->manualKernOnly = iTrue; | 293 | font->manualKernOnly = iTrue; |
281 | } | 294 | } |
@@ -423,7 +436,7 @@ static void freeBmp_(void *ptr) { | |||
423 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) { | 436 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) { |
424 | int w, h; | 437 | int w, h; |
425 | uint8_t *bmp = stbtt_GetGlyphBitmapSubpixel( | 438 | uint8_t *bmp = stbtt_GetGlyphBitmapSubpixel( |
426 | &d->font, d->scale, d->scale, xShift, 0.0f, glyphIndex, &w, &h, 0, 0); | 439 | &d->font, d->xScale, d->yScale, xShift, 0.0f, glyphIndex, &w, &h, 0, 0); |
427 | collect_Garbage(bmp, freeBmp_); /* `bmp` must be freed afterwards. */ | 440 | collect_Garbage(bmp, freeBmp_); /* `bmp` must be freed afterwards. */ |
428 | SDL_Surface *surface8 = | 441 | SDL_Surface *surface8 = |
429 | SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); | 442 | SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); |
@@ -478,12 +491,12 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
478 | int adv; | 491 | int adv; |
479 | const uint32_t gIndex = glyph->glyphIndex; | 492 | const uint32_t gIndex = glyph->glyphIndex; |
480 | stbtt_GetGlyphHMetrics(&d->font, gIndex, &adv, NULL); | 493 | stbtt_GetGlyphHMetrics(&d->font, gIndex, &adv, NULL); |
481 | glyph->advance = d->scale * adv; | 494 | glyph->advance = d->xScale * adv; |
482 | } | 495 | } |
483 | stbtt_GetGlyphBitmapBoxSubpixel(&d->font, | 496 | stbtt_GetGlyphBitmapBoxSubpixel(&d->font, |
484 | glyph->glyphIndex, | 497 | glyph->glyphIndex, |
485 | d->scale, | 498 | d->xScale, |
486 | d->scale, | 499 | d->yScale, |
487 | hoff * 0.5f, | 500 | hoff * 0.5f, |
488 | 0.0f, | 501 | 0.0f, |
489 | &glyph->d[hoff].x, | 502 | &glyph->d[hoff].x, |
@@ -734,11 +747,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
734 | } | 747 | } |
735 | const iBool useMonoAdvance = | 748 | const iBool useMonoAdvance = |
736 | monoAdvance > 0 && !isJapanese_FontId(fontId_Text_(glyph->font)); | 749 | monoAdvance > 0 && !isJapanese_FontId(fontId_Text_(glyph->font)); |
737 | /* The -0.25f is to mitigate issues with box-drawing characters sometimes rounding | 750 | const float advance = (useMonoAdvance ? monoAdvance : glyph->advance); |
738 | up to leave a hairline gap with the previous character. The purpose is to overlap | ||
739 | the glyphs slightly, since they are rendered antialiased and unhinted, which means | ||
740 | the right edge pixels may end up partially non-opaque. */ | ||
741 | const float advance = (useMonoAdvance ? monoAdvance - 0.25f : glyph->advance); | ||
742 | if (!isMeasuring_(mode)) { | 751 | if (!isMeasuring_(mode)) { |
743 | if (useMonoAdvance && dst.w > advance && glyph->font != d) { | 752 | if (useMonoAdvance && dst.w > advance && glyph->font != d) { |
744 | /* Glyphs from a different font may need recentering to look better. */ | 753 | /* Glyphs from a different font may need recentering to look better. */ |
@@ -769,7 +778,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
769 | const char *peek = chPos; | 778 | const char *peek = chPos; |
770 | const iChar next = nextChar_(&peek, text.end); | 779 | const iChar next = nextChar_(&peek, text.end); |
771 | if (enableKerning_Text && !d->manualKernOnly && next) { | 780 | if (enableKerning_Text && !d->manualKernOnly && next) { |
772 | xpos += d->scale * stbtt_GetGlyphKernAdvance(&d->font, glyph->glyphIndex, next); | 781 | xpos += d->xScale * stbtt_GetGlyphKernAdvance(&d->font, glyph->glyphIndex, next); |
773 | } | 782 | } |
774 | } | 783 | } |
775 | #endif | 784 | #endif |